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
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
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 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
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{2,4})"};
1391     
1392     
1393     case "P":
1394         return {g:1,
1395                 c:[
1396                    "o = results[", currentGroup, "];\n",
1397                    "var sn = o.substring(0,1);\n",
1398                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1399                    "var mn = o.substring(4,6) % 60;\n",
1400                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1401                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1402             ].join(""),
1403             s:"([+\-]\\d{4})"};
1404     case "T":
1405         return {g:0,
1406             c:null,
1407             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1408     case "Z":
1409         return {g:1,
1410             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1411                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1412             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1413     default:
1414         return {g:0,
1415             c:null,
1416             s:String.escape(character)};
1417     }
1418 };
1419
1420 /**
1421  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1422  * @return {String} The abbreviated timezone name (e.g. 'CST')
1423  */
1424 Date.prototype.getTimezone = function() {
1425     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1426 };
1427
1428 /**
1429  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1430  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1431  */
1432 Date.prototype.getGMTOffset = function() {
1433     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1434         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1435         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1436 };
1437
1438 /**
1439  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1440  * @return {String} 2-characters representing hours and 2-characters representing minutes
1441  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1442  */
1443 Date.prototype.getGMTColonOffset = function() {
1444         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1445                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1446                 + ":"
1447                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1448 }
1449
1450 /**
1451  * Get the numeric day number of the year, adjusted for leap year.
1452  * @return {Number} 0 through 364 (365 in leap years)
1453  */
1454 Date.prototype.getDayOfYear = function() {
1455     var num = 0;
1456     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1457     for (var i = 0; i < this.getMonth(); ++i) {
1458         num += Date.daysInMonth[i];
1459     }
1460     return num + this.getDate() - 1;
1461 };
1462
1463 /**
1464  * Get the string representation of the numeric week number of the year
1465  * (equivalent to the format specifier 'W').
1466  * @return {String} '00' through '52'
1467  */
1468 Date.prototype.getWeekOfYear = function() {
1469     // Skip to Thursday of this week
1470     var now = this.getDayOfYear() + (4 - this.getDay());
1471     // Find the first Thursday of the year
1472     var jan1 = new Date(this.getFullYear(), 0, 1);
1473     var then = (7 - jan1.getDay() + 4);
1474     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1475 };
1476
1477 /**
1478  * Whether or not the current date is in a leap year.
1479  * @return {Boolean} True if the current date is in a leap year, else false
1480  */
1481 Date.prototype.isLeapYear = function() {
1482     var year = this.getFullYear();
1483     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1484 };
1485
1486 /**
1487  * Get the first day of the current month, adjusted for leap year.  The returned value
1488  * is the numeric day index within the week (0-6) which can be used in conjunction with
1489  * the {@link #monthNames} array to retrieve the textual day name.
1490  * Example:
1491  *<pre><code>
1492 var dt = new Date('1/10/2007');
1493 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1494 </code></pre>
1495  * @return {Number} The day number (0-6)
1496  */
1497 Date.prototype.getFirstDayOfMonth = function() {
1498     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1499     return (day < 0) ? (day + 7) : day;
1500 };
1501
1502 /**
1503  * Get the last day of the current month, adjusted for leap year.  The returned value
1504  * is the numeric day index within the week (0-6) which can be used in conjunction with
1505  * the {@link #monthNames} array to retrieve the textual day name.
1506  * Example:
1507  *<pre><code>
1508 var dt = new Date('1/10/2007');
1509 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1510 </code></pre>
1511  * @return {Number} The day number (0-6)
1512  */
1513 Date.prototype.getLastDayOfMonth = function() {
1514     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1515     return (day < 0) ? (day + 7) : day;
1516 };
1517
1518
1519 /**
1520  * Get the first date of this date's month
1521  * @return {Date}
1522  */
1523 Date.prototype.getFirstDateOfMonth = function() {
1524     return new Date(this.getFullYear(), this.getMonth(), 1);
1525 };
1526
1527 /**
1528  * Get the last date of this date's month
1529  * @return {Date}
1530  */
1531 Date.prototype.getLastDateOfMonth = function() {
1532     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1533 };
1534 /**
1535  * Get the number of days in the current month, adjusted for leap year.
1536  * @return {Number} The number of days in the month
1537  */
1538 Date.prototype.getDaysInMonth = function() {
1539     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1540     return Date.daysInMonth[this.getMonth()];
1541 };
1542
1543 /**
1544  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1545  * @return {String} 'st, 'nd', 'rd' or 'th'
1546  */
1547 Date.prototype.getSuffix = function() {
1548     switch (this.getDate()) {
1549         case 1:
1550         case 21:
1551         case 31:
1552             return "st";
1553         case 2:
1554         case 22:
1555             return "nd";
1556         case 3:
1557         case 23:
1558             return "rd";
1559         default:
1560             return "th";
1561     }
1562 };
1563
1564 // private
1565 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1566
1567 /**
1568  * An array of textual month names.
1569  * Override these values for international dates, for example...
1570  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1571  * @type Array
1572  * @static
1573  */
1574 Date.monthNames =
1575    ["January",
1576     "February",
1577     "March",
1578     "April",
1579     "May",
1580     "June",
1581     "July",
1582     "August",
1583     "September",
1584     "October",
1585     "November",
1586     "December"];
1587
1588 /**
1589  * An array of textual day names.
1590  * Override these values for international dates, for example...
1591  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1592  * @type Array
1593  * @static
1594  */
1595 Date.dayNames =
1596    ["Sunday",
1597     "Monday",
1598     "Tuesday",
1599     "Wednesday",
1600     "Thursday",
1601     "Friday",
1602     "Saturday"];
1603
1604 // private
1605 Date.y2kYear = 50;
1606 // private
1607 Date.monthNumbers = {
1608     Jan:0,
1609     Feb:1,
1610     Mar:2,
1611     Apr:3,
1612     May:4,
1613     Jun:5,
1614     Jul:6,
1615     Aug:7,
1616     Sep:8,
1617     Oct:9,
1618     Nov:10,
1619     Dec:11};
1620
1621 /**
1622  * Creates and returns a new Date instance with the exact same date value as the called instance.
1623  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1624  * variable will also be changed.  When the intention is to create a new variable that will not
1625  * modify the original instance, you should create a clone.
1626  *
1627  * Example of correctly cloning a date:
1628  * <pre><code>
1629 //wrong way:
1630 var orig = new Date('10/1/2006');
1631 var copy = orig;
1632 copy.setDate(5);
1633 document.write(orig);  //returns 'Thu Oct 05 2006'!
1634
1635 //correct way:
1636 var orig = new Date('10/1/2006');
1637 var copy = orig.clone();
1638 copy.setDate(5);
1639 document.write(orig);  //returns 'Thu Oct 01 2006'
1640 </code></pre>
1641  * @return {Date} The new Date instance
1642  */
1643 Date.prototype.clone = function() {
1644         return new Date(this.getTime());
1645 };
1646
1647 /**
1648  * Clears any time information from this date
1649  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1650  @return {Date} this or the clone
1651  */
1652 Date.prototype.clearTime = function(clone){
1653     if(clone){
1654         return this.clone().clearTime();
1655     }
1656     this.setHours(0);
1657     this.setMinutes(0);
1658     this.setSeconds(0);
1659     this.setMilliseconds(0);
1660     return this;
1661 };
1662
1663 // private
1664 // safari setMonth is broken
1665 if(Roo.isSafari){
1666     Date.brokenSetMonth = Date.prototype.setMonth;
1667         Date.prototype.setMonth = function(num){
1668                 if(num <= -1){
1669                         var n = Math.ceil(-num);
1670                         var back_year = Math.ceil(n/12);
1671                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1672                         this.setFullYear(this.getFullYear() - back_year);
1673                         return Date.brokenSetMonth.call(this, month);
1674                 } else {
1675                         return Date.brokenSetMonth.apply(this, arguments);
1676                 }
1677         };
1678 }
1679
1680 /** Date interval constant 
1681 * @static 
1682 * @type String */
1683 Date.MILLI = "ms";
1684 /** Date interval constant 
1685 * @static 
1686 * @type String */
1687 Date.SECOND = "s";
1688 /** Date interval constant 
1689 * @static 
1690 * @type String */
1691 Date.MINUTE = "mi";
1692 /** Date interval constant 
1693 * @static 
1694 * @type String */
1695 Date.HOUR = "h";
1696 /** Date interval constant 
1697 * @static 
1698 * @type String */
1699 Date.DAY = "d";
1700 /** Date interval constant 
1701 * @static 
1702 * @type String */
1703 Date.MONTH = "mo";
1704 /** Date interval constant 
1705 * @static 
1706 * @type String */
1707 Date.YEAR = "y";
1708
1709 /**
1710  * Provides a convenient method of performing basic date arithmetic.  This method
1711  * does not modify the Date instance being called - it creates and returns
1712  * a new Date instance containing the resulting date value.
1713  *
1714  * Examples:
1715  * <pre><code>
1716 //Basic usage:
1717 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1718 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1719
1720 //Negative values will subtract correctly:
1721 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1722 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1723
1724 //You can even chain several calls together in one line!
1725 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1726 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1727  </code></pre>
1728  *
1729  * @param {String} interval   A valid date interval enum value
1730  * @param {Number} value      The amount to add to the current date
1731  * @return {Date} The new Date instance
1732  */
1733 Date.prototype.add = function(interval, value){
1734   var d = this.clone();
1735   if (!interval || value === 0) return d;
1736   switch(interval.toLowerCase()){
1737     case Date.MILLI:
1738       d.setMilliseconds(this.getMilliseconds() + value);
1739       break;
1740     case Date.SECOND:
1741       d.setSeconds(this.getSeconds() + value);
1742       break;
1743     case Date.MINUTE:
1744       d.setMinutes(this.getMinutes() + value);
1745       break;
1746     case Date.HOUR:
1747       d.setHours(this.getHours() + value);
1748       break;
1749     case Date.DAY:
1750       d.setDate(this.getDate() + value);
1751       break;
1752     case Date.MONTH:
1753       var day = this.getDate();
1754       if(day > 28){
1755           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1756       }
1757       d.setDate(day);
1758       d.setMonth(this.getMonth() + value);
1759       break;
1760     case Date.YEAR:
1761       d.setFullYear(this.getFullYear() + value);
1762       break;
1763   }
1764   return d;
1765 };
1766 /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776
1777 /**
1778  * @class Roo.lib.Dom
1779  * @static
1780  * 
1781  * Dom utils (from YIU afaik)
1782  * 
1783  **/
1784 Roo.lib.Dom = {
1785     /**
1786      * Get the view width
1787      * @param {Boolean} full True will get the full document, otherwise it's the view width
1788      * @return {Number} The width
1789      */
1790      
1791     getViewWidth : function(full) {
1792         return full ? this.getDocumentWidth() : this.getViewportWidth();
1793     },
1794     /**
1795      * Get the view height
1796      * @param {Boolean} full True will get the full document, otherwise it's the view height
1797      * @return {Number} The height
1798      */
1799     getViewHeight : function(full) {
1800         return full ? this.getDocumentHeight() : this.getViewportHeight();
1801     },
1802
1803     getDocumentHeight: function() {
1804         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1805         return Math.max(scrollHeight, this.getViewportHeight());
1806     },
1807
1808     getDocumentWidth: function() {
1809         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1810         return Math.max(scrollWidth, this.getViewportWidth());
1811     },
1812
1813     getViewportHeight: function() {
1814         var height = self.innerHeight;
1815         var mode = document.compatMode;
1816
1817         if ((mode || Roo.isIE) && !Roo.isOpera) {
1818             height = (mode == "CSS1Compat") ?
1819                      document.documentElement.clientHeight :
1820                      document.body.clientHeight;
1821         }
1822
1823         return height;
1824     },
1825
1826     getViewportWidth: function() {
1827         var width = self.innerWidth;
1828         var mode = document.compatMode;
1829
1830         if (mode || Roo.isIE) {
1831             width = (mode == "CSS1Compat") ?
1832                     document.documentElement.clientWidth :
1833                     document.body.clientWidth;
1834         }
1835         return width;
1836     },
1837
1838     isAncestor : function(p, c) {
1839         p = Roo.getDom(p);
1840         c = Roo.getDom(c);
1841         if (!p || !c) {
1842             return false;
1843         }
1844
1845         if (p.contains && !Roo.isSafari) {
1846             return p.contains(c);
1847         } else if (p.compareDocumentPosition) {
1848             return !!(p.compareDocumentPosition(c) & 16);
1849         } else {
1850             var parent = c.parentNode;
1851             while (parent) {
1852                 if (parent == p) {
1853                     return true;
1854                 }
1855                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1856                     return false;
1857                 }
1858                 parent = parent.parentNode;
1859             }
1860             return false;
1861         }
1862     },
1863
1864     getRegion : function(el) {
1865         return Roo.lib.Region.getRegion(el);
1866     },
1867
1868     getY : function(el) {
1869         return this.getXY(el)[1];
1870     },
1871
1872     getX : function(el) {
1873         return this.getXY(el)[0];
1874     },
1875
1876     getXY : function(el) {
1877         var p, pe, b, scroll, bd = document.body;
1878         el = Roo.getDom(el);
1879         var fly = Roo.lib.AnimBase.fly;
1880         if (el.getBoundingClientRect) {
1881             b = el.getBoundingClientRect();
1882             scroll = fly(document).getScroll();
1883             return [b.left + scroll.left, b.top + scroll.top];
1884         }
1885         var x = 0, y = 0;
1886
1887         p = el;
1888
1889         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1890
1891         while (p) {
1892
1893             x += p.offsetLeft;
1894             y += p.offsetTop;
1895
1896             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1897                 hasAbsolute = true;
1898             }
1899
1900             if (Roo.isGecko) {
1901                 pe = fly(p);
1902
1903                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1904                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1905
1906
1907                 x += bl;
1908                 y += bt;
1909
1910
1911                 if (p != el && pe.getStyle('overflow') != 'visible') {
1912                     x += bl;
1913                     y += bt;
1914                 }
1915             }
1916             p = p.offsetParent;
1917         }
1918
1919         if (Roo.isSafari && hasAbsolute) {
1920             x -= bd.offsetLeft;
1921             y -= bd.offsetTop;
1922         }
1923
1924         if (Roo.isGecko && !hasAbsolute) {
1925             var dbd = fly(bd);
1926             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1927             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1928         }
1929
1930         p = el.parentNode;
1931         while (p && p != bd) {
1932             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1933                 x -= p.scrollLeft;
1934                 y -= p.scrollTop;
1935             }
1936             p = p.parentNode;
1937         }
1938         return [x, y];
1939     },
1940  
1941   
1942
1943
1944     setXY : function(el, xy) {
1945         el = Roo.fly(el, '_setXY');
1946         el.position();
1947         var pts = el.translatePoints(xy);
1948         if (xy[0] !== false) {
1949             el.dom.style.left = pts.left + "px";
1950         }
1951         if (xy[1] !== false) {
1952             el.dom.style.top = pts.top + "px";
1953         }
1954     },
1955
1956     setX : function(el, x) {
1957         this.setXY(el, [x, false]);
1958     },
1959
1960     setY : function(el, y) {
1961         this.setXY(el, [false, y]);
1962     }
1963 };
1964 /*
1965  * Portions of this file are based on pieces of Yahoo User Interface Library
1966  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1967  * YUI licensed under the BSD License:
1968  * http://developer.yahoo.net/yui/license.txt
1969  * <script type="text/javascript">
1970  *
1971  */
1972
1973 Roo.lib.Event = function() {
1974     var loadComplete = false;
1975     var listeners = [];
1976     var unloadListeners = [];
1977     var retryCount = 0;
1978     var onAvailStack = [];
1979     var counter = 0;
1980     var lastError = null;
1981
1982     return {
1983         POLL_RETRYS: 200,
1984         POLL_INTERVAL: 20,
1985         EL: 0,
1986         TYPE: 1,
1987         FN: 2,
1988         WFN: 3,
1989         OBJ: 3,
1990         ADJ_SCOPE: 4,
1991         _interval: null,
1992
1993         startInterval: function() {
1994             if (!this._interval) {
1995                 var self = this;
1996                 var callback = function() {
1997                     self._tryPreloadAttach();
1998                 };
1999                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2000
2001             }
2002         },
2003
2004         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2005             onAvailStack.push({ id:         p_id,
2006                 fn:         p_fn,
2007                 obj:        p_obj,
2008                 override:   p_override,
2009                 checkReady: false    });
2010
2011             retryCount = this.POLL_RETRYS;
2012             this.startInterval();
2013         },
2014
2015
2016         addListener: function(el, eventName, fn) {
2017             el = Roo.getDom(el);
2018             if (!el || !fn) {
2019                 return false;
2020             }
2021
2022             if ("unload" == eventName) {
2023                 unloadListeners[unloadListeners.length] =
2024                 [el, eventName, fn];
2025                 return true;
2026             }
2027
2028             var wrappedFn = function(e) {
2029                 return fn(Roo.lib.Event.getEvent(e));
2030             };
2031
2032             var li = [el, eventName, fn, wrappedFn];
2033
2034             var index = listeners.length;
2035             listeners[index] = li;
2036
2037             this.doAdd(el, eventName, wrappedFn, false);
2038             return true;
2039
2040         },
2041
2042
2043         removeListener: function(el, eventName, fn) {
2044             var i, len;
2045
2046             el = Roo.getDom(el);
2047
2048             if(!fn) {
2049                 return this.purgeElement(el, false, eventName);
2050             }
2051
2052
2053             if ("unload" == eventName) {
2054
2055                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2056                     var li = unloadListeners[i];
2057                     if (li &&
2058                         li[0] == el &&
2059                         li[1] == eventName &&
2060                         li[2] == fn) {
2061                         unloadListeners.splice(i, 1);
2062                         return true;
2063                     }
2064                 }
2065
2066                 return false;
2067             }
2068
2069             var cacheItem = null;
2070
2071
2072             var index = arguments[3];
2073
2074             if ("undefined" == typeof index) {
2075                 index = this._getCacheIndex(el, eventName, fn);
2076             }
2077
2078             if (index >= 0) {
2079                 cacheItem = listeners[index];
2080             }
2081
2082             if (!el || !cacheItem) {
2083                 return false;
2084             }
2085
2086             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2087
2088             delete listeners[index][this.WFN];
2089             delete listeners[index][this.FN];
2090             listeners.splice(index, 1);
2091
2092             return true;
2093
2094         },
2095
2096
2097         getTarget: function(ev, resolveTextNode) {
2098             ev = ev.browserEvent || ev;
2099             var t = ev.target || ev.srcElement;
2100             return this.resolveTextNode(t);
2101         },
2102
2103
2104         resolveTextNode: function(node) {
2105             if (Roo.isSafari && node && 3 == node.nodeType) {
2106                 return node.parentNode;
2107             } else {
2108                 return node;
2109             }
2110         },
2111
2112
2113         getPageX: function(ev) {
2114             ev = ev.browserEvent || ev;
2115             var x = ev.pageX;
2116             if (!x && 0 !== x) {
2117                 x = ev.clientX || 0;
2118
2119                 if (Roo.isIE) {
2120                     x += this.getScroll()[1];
2121                 }
2122             }
2123
2124             return x;
2125         },
2126
2127
2128         getPageY: function(ev) {
2129             ev = ev.browserEvent || ev;
2130             var y = ev.pageY;
2131             if (!y && 0 !== y) {
2132                 y = ev.clientY || 0;
2133
2134                 if (Roo.isIE) {
2135                     y += this.getScroll()[0];
2136                 }
2137             }
2138
2139
2140             return y;
2141         },
2142
2143
2144         getXY: function(ev) {
2145             ev = ev.browserEvent || ev;
2146             return [this.getPageX(ev), this.getPageY(ev)];
2147         },
2148
2149
2150         getRelatedTarget: function(ev) {
2151             ev = ev.browserEvent || ev;
2152             var t = ev.relatedTarget;
2153             if (!t) {
2154                 if (ev.type == "mouseout") {
2155                     t = ev.toElement;
2156                 } else if (ev.type == "mouseover") {
2157                     t = ev.fromElement;
2158                 }
2159             }
2160
2161             return this.resolveTextNode(t);
2162         },
2163
2164
2165         getTime: function(ev) {
2166             ev = ev.browserEvent || ev;
2167             if (!ev.time) {
2168                 var t = new Date().getTime();
2169                 try {
2170                     ev.time = t;
2171                 } catch(ex) {
2172                     this.lastError = ex;
2173                     return t;
2174                 }
2175             }
2176
2177             return ev.time;
2178         },
2179
2180
2181         stopEvent: function(ev) {
2182             this.stopPropagation(ev);
2183             this.preventDefault(ev);
2184         },
2185
2186
2187         stopPropagation: function(ev) {
2188             ev = ev.browserEvent || ev;
2189             if (ev.stopPropagation) {
2190                 ev.stopPropagation();
2191             } else {
2192                 ev.cancelBubble = true;
2193             }
2194         },
2195
2196
2197         preventDefault: function(ev) {
2198             ev = ev.browserEvent || ev;
2199             if(ev.preventDefault) {
2200                 ev.preventDefault();
2201             } else {
2202                 ev.returnValue = false;
2203             }
2204         },
2205
2206
2207         getEvent: function(e) {
2208             var ev = e || window.event;
2209             if (!ev) {
2210                 var c = this.getEvent.caller;
2211                 while (c) {
2212                     ev = c.arguments[0];
2213                     if (ev && Event == ev.constructor) {
2214                         break;
2215                     }
2216                     c = c.caller;
2217                 }
2218             }
2219             return ev;
2220         },
2221
2222
2223         getCharCode: function(ev) {
2224             ev = ev.browserEvent || ev;
2225             return ev.charCode || ev.keyCode || 0;
2226         },
2227
2228
2229         _getCacheIndex: function(el, eventName, fn) {
2230             for (var i = 0,len = listeners.length; i < len; ++i) {
2231                 var li = listeners[i];
2232                 if (li &&
2233                     li[this.FN] == fn &&
2234                     li[this.EL] == el &&
2235                     li[this.TYPE] == eventName) {
2236                     return i;
2237                 }
2238             }
2239
2240             return -1;
2241         },
2242
2243
2244         elCache: {},
2245
2246
2247         getEl: function(id) {
2248             return document.getElementById(id);
2249         },
2250
2251
2252         clearCache: function() {
2253         },
2254
2255
2256         _load: function(e) {
2257             loadComplete = true;
2258             var EU = Roo.lib.Event;
2259
2260
2261             if (Roo.isIE) {
2262                 EU.doRemove(window, "load", EU._load);
2263             }
2264         },
2265
2266
2267         _tryPreloadAttach: function() {
2268
2269             if (this.locked) {
2270                 return false;
2271             }
2272
2273             this.locked = true;
2274
2275
2276             var tryAgain = !loadComplete;
2277             if (!tryAgain) {
2278                 tryAgain = (retryCount > 0);
2279             }
2280
2281
2282             var notAvail = [];
2283             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2284                 var item = onAvailStack[i];
2285                 if (item) {
2286                     var el = this.getEl(item.id);
2287
2288                     if (el) {
2289                         if (!item.checkReady ||
2290                             loadComplete ||
2291                             el.nextSibling ||
2292                             (document && document.body)) {
2293
2294                             var scope = el;
2295                             if (item.override) {
2296                                 if (item.override === true) {
2297                                     scope = item.obj;
2298                                 } else {
2299                                     scope = item.override;
2300                                 }
2301                             }
2302                             item.fn.call(scope, item.obj);
2303                             onAvailStack[i] = null;
2304                         }
2305                     } else {
2306                         notAvail.push(item);
2307                     }
2308                 }
2309             }
2310
2311             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2312
2313             if (tryAgain) {
2314
2315                 this.startInterval();
2316             } else {
2317                 clearInterval(this._interval);
2318                 this._interval = null;
2319             }
2320
2321             this.locked = false;
2322
2323             return true;
2324
2325         },
2326
2327
2328         purgeElement: function(el, recurse, eventName) {
2329             var elListeners = this.getListeners(el, eventName);
2330             if (elListeners) {
2331                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2332                     var l = elListeners[i];
2333                     this.removeListener(el, l.type, l.fn);
2334                 }
2335             }
2336
2337             if (recurse && el && el.childNodes) {
2338                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2339                     this.purgeElement(el.childNodes[i], recurse, eventName);
2340                 }
2341             }
2342         },
2343
2344
2345         getListeners: function(el, eventName) {
2346             var results = [], searchLists;
2347             if (!eventName) {
2348                 searchLists = [listeners, unloadListeners];
2349             } else if (eventName == "unload") {
2350                 searchLists = [unloadListeners];
2351             } else {
2352                 searchLists = [listeners];
2353             }
2354
2355             for (var j = 0; j < searchLists.length; ++j) {
2356                 var searchList = searchLists[j];
2357                 if (searchList && searchList.length > 0) {
2358                     for (var i = 0,len = searchList.length; i < len; ++i) {
2359                         var l = searchList[i];
2360                         if (l && l[this.EL] === el &&
2361                             (!eventName || eventName === l[this.TYPE])) {
2362                             results.push({
2363                                 type:   l[this.TYPE],
2364                                 fn:     l[this.FN],
2365                                 obj:    l[this.OBJ],
2366                                 adjust: l[this.ADJ_SCOPE],
2367                                 index:  i
2368                             });
2369                         }
2370                     }
2371                 }
2372             }
2373
2374             return (results.length) ? results : null;
2375         },
2376
2377
2378         _unload: function(e) {
2379
2380             var EU = Roo.lib.Event, i, j, l, len, index;
2381
2382             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2383                 l = unloadListeners[i];
2384                 if (l) {
2385                     var scope = window;
2386                     if (l[EU.ADJ_SCOPE]) {
2387                         if (l[EU.ADJ_SCOPE] === true) {
2388                             scope = l[EU.OBJ];
2389                         } else {
2390                             scope = l[EU.ADJ_SCOPE];
2391                         }
2392                     }
2393                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2394                     unloadListeners[i] = null;
2395                     l = null;
2396                     scope = null;
2397                 }
2398             }
2399
2400             unloadListeners = null;
2401
2402             if (listeners && listeners.length > 0) {
2403                 j = listeners.length;
2404                 while (j) {
2405                     index = j - 1;
2406                     l = listeners[index];
2407                     if (l) {
2408                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2409                                 l[EU.FN], index);
2410                     }
2411                     j = j - 1;
2412                 }
2413                 l = null;
2414
2415                 EU.clearCache();
2416             }
2417
2418             EU.doRemove(window, "unload", EU._unload);
2419
2420         },
2421
2422
2423         getScroll: function() {
2424             var dd = document.documentElement, db = document.body;
2425             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2426                 return [dd.scrollTop, dd.scrollLeft];
2427             } else if (db) {
2428                 return [db.scrollTop, db.scrollLeft];
2429             } else {
2430                 return [0, 0];
2431             }
2432         },
2433
2434
2435         doAdd: function () {
2436             if (window.addEventListener) {
2437                 return function(el, eventName, fn, capture) {
2438                     el.addEventListener(eventName, fn, (capture));
2439                 };
2440             } else if (window.attachEvent) {
2441                 return function(el, eventName, fn, capture) {
2442                     el.attachEvent("on" + eventName, fn);
2443                 };
2444             } else {
2445                 return function() {
2446                 };
2447             }
2448         }(),
2449
2450
2451         doRemove: function() {
2452             if (window.removeEventListener) {
2453                 return function (el, eventName, fn, capture) {
2454                     el.removeEventListener(eventName, fn, (capture));
2455                 };
2456             } else if (window.detachEvent) {
2457                 return function (el, eventName, fn) {
2458                     el.detachEvent("on" + eventName, fn);
2459                 };
2460             } else {
2461                 return function() {
2462                 };
2463             }
2464         }()
2465     };
2466     
2467 }();
2468 (function() {     
2469    
2470     var E = Roo.lib.Event;
2471     E.on = E.addListener;
2472     E.un = E.removeListener;
2473
2474     if (document && document.body) {
2475         E._load();
2476     } else {
2477         E.doAdd(window, "load", E._load);
2478     }
2479     E.doAdd(window, "unload", E._unload);
2480     E._tryPreloadAttach();
2481 })();
2482
2483 /*
2484  * Portions of this file are based on pieces of Yahoo User Interface Library
2485  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2486  * YUI licensed under the BSD License:
2487  * http://developer.yahoo.net/yui/license.txt
2488  * <script type="text/javascript">
2489  *
2490  */
2491
2492 (function() {
2493     /**
2494      * @class Roo.lib.Ajax
2495      *
2496      */
2497     Roo.lib.Ajax = {
2498         /**
2499          * @static 
2500          */
2501         request : function(method, uri, cb, data, options) {
2502             if(options){
2503                 var hs = options.headers;
2504                 if(hs){
2505                     for(var h in hs){
2506                         if(hs.hasOwnProperty(h)){
2507                             this.initHeader(h, hs[h], false);
2508                         }
2509                     }
2510                 }
2511                 if(options.xmlData){
2512                     this.initHeader('Content-Type', 'text/xml', false);
2513                     method = 'POST';
2514                     data = options.xmlData;
2515                 }
2516             }
2517
2518             return this.asyncRequest(method, uri, cb, data);
2519         },
2520
2521         serializeForm : function(form) {
2522             if(typeof form == 'string') {
2523                 form = (document.getElementById(form) || document.forms[form]);
2524             }
2525
2526             var el, name, val, disabled, data = '', hasSubmit = false;
2527             for (var i = 0; i < form.elements.length; i++) {
2528                 el = form.elements[i];
2529                 disabled = form.elements[i].disabled;
2530                 name = form.elements[i].name;
2531                 val = form.elements[i].value;
2532
2533                 if (!disabled && name){
2534                     switch (el.type)
2535                             {
2536                         case 'select-one':
2537                         case 'select-multiple':
2538                             for (var j = 0; j < el.options.length; j++) {
2539                                 if (el.options[j].selected) {
2540                                     if (Roo.isIE) {
2541                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2542                                     }
2543                                     else {
2544                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2545                                     }
2546                                 }
2547                             }
2548                             break;
2549                         case 'radio':
2550                         case 'checkbox':
2551                             if (el.checked) {
2552                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             }
2554                             break;
2555                         case 'file':
2556
2557                         case undefined:
2558
2559                         case 'reset':
2560
2561                         case 'button':
2562
2563                             break;
2564                         case 'submit':
2565                             if(hasSubmit == false) {
2566                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2567                                 hasSubmit = true;
2568                             }
2569                             break;
2570                         default:
2571                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2572                             break;
2573                     }
2574                 }
2575             }
2576             data = data.substr(0, data.length - 1);
2577             return data;
2578         },
2579
2580         headers:{},
2581
2582         hasHeaders:false,
2583
2584         useDefaultHeader:true,
2585
2586         defaultPostHeader:'application/x-www-form-urlencoded',
2587
2588         useDefaultXhrHeader:true,
2589
2590         defaultXhrHeader:'XMLHttpRequest',
2591
2592         hasDefaultHeaders:true,
2593
2594         defaultHeaders:{},
2595
2596         poll:{},
2597
2598         timeout:{},
2599
2600         pollInterval:50,
2601
2602         transactionId:0,
2603
2604         setProgId:function(id)
2605         {
2606             this.activeX.unshift(id);
2607         },
2608
2609         setDefaultPostHeader:function(b)
2610         {
2611             this.useDefaultHeader = b;
2612         },
2613
2614         setDefaultXhrHeader:function(b)
2615         {
2616             this.useDefaultXhrHeader = b;
2617         },
2618
2619         setPollingInterval:function(i)
2620         {
2621             if (typeof i == 'number' && isFinite(i)) {
2622                 this.pollInterval = i;
2623             }
2624         },
2625
2626         createXhrObject:function(transactionId)
2627         {
2628             var obj,http;
2629             try
2630             {
2631
2632                 http = new XMLHttpRequest();
2633
2634                 obj = { conn:http, tId:transactionId };
2635             }
2636             catch(e)
2637             {
2638                 for (var i = 0; i < this.activeX.length; ++i) {
2639                     try
2640                     {
2641
2642                         http = new ActiveXObject(this.activeX[i]);
2643
2644                         obj = { conn:http, tId:transactionId };
2645                         break;
2646                     }
2647                     catch(e) {
2648                     }
2649                 }
2650             }
2651             finally
2652             {
2653                 return obj;
2654             }
2655         },
2656
2657         getConnectionObject:function()
2658         {
2659             var o;
2660             var tId = this.transactionId;
2661
2662             try
2663             {
2664                 o = this.createXhrObject(tId);
2665                 if (o) {
2666                     this.transactionId++;
2667                 }
2668             }
2669             catch(e) {
2670             }
2671             finally
2672             {
2673                 return o;
2674             }
2675         },
2676
2677         asyncRequest:function(method, uri, callback, postData)
2678         {
2679             var o = this.getConnectionObject();
2680
2681             if (!o) {
2682                 return null;
2683             }
2684             else {
2685                 o.conn.open(method, uri, true);
2686
2687                 if (this.useDefaultXhrHeader) {
2688                     if (!this.defaultHeaders['X-Requested-With']) {
2689                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2690                     }
2691                 }
2692
2693                 if(postData && this.useDefaultHeader){
2694                     this.initHeader('Content-Type', this.defaultPostHeader);
2695                 }
2696
2697                  if (this.hasDefaultHeaders || this.hasHeaders) {
2698                     this.setHeader(o);
2699                 }
2700
2701                 this.handleReadyState(o, callback);
2702                 o.conn.send(postData || null);
2703
2704                 return o;
2705             }
2706         },
2707
2708         handleReadyState:function(o, callback)
2709         {
2710             var oConn = this;
2711
2712             if (callback && callback.timeout) {
2713                 
2714                 this.timeout[o.tId] = window.setTimeout(function() {
2715                     oConn.abort(o, callback, true);
2716                 }, callback.timeout);
2717             }
2718
2719             this.poll[o.tId] = window.setInterval(
2720                     function() {
2721                         if (o.conn && o.conn.readyState == 4) {
2722                             window.clearInterval(oConn.poll[o.tId]);
2723                             delete oConn.poll[o.tId];
2724
2725                             if(callback && callback.timeout) {
2726                                 window.clearTimeout(oConn.timeout[o.tId]);
2727                                 delete oConn.timeout[o.tId];
2728                             }
2729
2730                             oConn.handleTransactionResponse(o, callback);
2731                         }
2732                     }
2733                     , this.pollInterval);
2734         },
2735
2736         handleTransactionResponse:function(o, callback, isAbort)
2737         {
2738
2739             if (!callback) {
2740                 this.releaseObject(o);
2741                 return;
2742             }
2743
2744             var httpStatus, responseObject;
2745
2746             try
2747             {
2748                 if (o.conn.status !== undefined && o.conn.status != 0) {
2749                     httpStatus = o.conn.status;
2750                 }
2751                 else {
2752                     httpStatus = 13030;
2753                 }
2754             }
2755             catch(e) {
2756
2757
2758                 httpStatus = 13030;
2759             }
2760
2761             if (httpStatus >= 200 && httpStatus < 300) {
2762                 responseObject = this.createResponseObject(o, callback.argument);
2763                 if (callback.success) {
2764                     if (!callback.scope) {
2765                         callback.success(responseObject);
2766                     }
2767                     else {
2768
2769
2770                         callback.success.apply(callback.scope, [responseObject]);
2771                     }
2772                 }
2773             }
2774             else {
2775                 switch (httpStatus) {
2776
2777                     case 12002:
2778                     case 12029:
2779                     case 12030:
2780                     case 12031:
2781                     case 12152:
2782                     case 13030:
2783                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2784                         if (callback.failure) {
2785                             if (!callback.scope) {
2786                                 callback.failure(responseObject);
2787                             }
2788                             else {
2789                                 callback.failure.apply(callback.scope, [responseObject]);
2790                             }
2791                         }
2792                         break;
2793                     default:
2794                         responseObject = this.createResponseObject(o, callback.argument);
2795                         if (callback.failure) {
2796                             if (!callback.scope) {
2797                                 callback.failure(responseObject);
2798                             }
2799                             else {
2800                                 callback.failure.apply(callback.scope, [responseObject]);
2801                             }
2802                         }
2803                 }
2804             }
2805
2806             this.releaseObject(o);
2807             responseObject = null;
2808         },
2809
2810         createResponseObject:function(o, callbackArg)
2811         {
2812             var obj = {};
2813             var headerObj = {};
2814
2815             try
2816             {
2817                 var headerStr = o.conn.getAllResponseHeaders();
2818                 var header = headerStr.split('\n');
2819                 for (var i = 0; i < header.length; i++) {
2820                     var delimitPos = header[i].indexOf(':');
2821                     if (delimitPos != -1) {
2822                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2823                     }
2824                 }
2825             }
2826             catch(e) {
2827             }
2828
2829             obj.tId = o.tId;
2830             obj.status = o.conn.status;
2831             obj.statusText = o.conn.statusText;
2832             obj.getResponseHeader = headerObj;
2833             obj.getAllResponseHeaders = headerStr;
2834             obj.responseText = o.conn.responseText;
2835             obj.responseXML = o.conn.responseXML;
2836
2837             if (typeof callbackArg !== undefined) {
2838                 obj.argument = callbackArg;
2839             }
2840
2841             return obj;
2842         },
2843
2844         createExceptionObject:function(tId, callbackArg, isAbort)
2845         {
2846             var COMM_CODE = 0;
2847             var COMM_ERROR = 'communication failure';
2848             var ABORT_CODE = -1;
2849             var ABORT_ERROR = 'transaction aborted';
2850
2851             var obj = {};
2852
2853             obj.tId = tId;
2854             if (isAbort) {
2855                 obj.status = ABORT_CODE;
2856                 obj.statusText = ABORT_ERROR;
2857             }
2858             else {
2859                 obj.status = COMM_CODE;
2860                 obj.statusText = COMM_ERROR;
2861             }
2862
2863             if (callbackArg) {
2864                 obj.argument = callbackArg;
2865             }
2866
2867             return obj;
2868         },
2869
2870         initHeader:function(label, value, isDefault)
2871         {
2872             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2873
2874             if (headerObj[label] === undefined) {
2875                 headerObj[label] = value;
2876             }
2877             else {
2878
2879
2880                 headerObj[label] = value + "," + headerObj[label];
2881             }
2882
2883             if (isDefault) {
2884                 this.hasDefaultHeaders = true;
2885             }
2886             else {
2887                 this.hasHeaders = true;
2888             }
2889         },
2890
2891
2892         setHeader:function(o)
2893         {
2894             if (this.hasDefaultHeaders) {
2895                 for (var prop in this.defaultHeaders) {
2896                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2897                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2898                     }
2899                 }
2900             }
2901
2902             if (this.hasHeaders) {
2903                 for (var prop in this.headers) {
2904                     if (this.headers.hasOwnProperty(prop)) {
2905                         o.conn.setRequestHeader(prop, this.headers[prop]);
2906                     }
2907                 }
2908                 this.headers = {};
2909                 this.hasHeaders = false;
2910             }
2911         },
2912
2913         resetDefaultHeaders:function() {
2914             delete this.defaultHeaders;
2915             this.defaultHeaders = {};
2916             this.hasDefaultHeaders = false;
2917         },
2918
2919         abort:function(o, callback, isTimeout)
2920         {
2921             if(this.isCallInProgress(o)) {
2922                 o.conn.abort();
2923                 window.clearInterval(this.poll[o.tId]);
2924                 delete this.poll[o.tId];
2925                 if (isTimeout) {
2926                     delete this.timeout[o.tId];
2927                 }
2928
2929                 this.handleTransactionResponse(o, callback, true);
2930
2931                 return true;
2932             }
2933             else {
2934                 return false;
2935             }
2936         },
2937
2938
2939         isCallInProgress:function(o)
2940         {
2941             if (o && o.conn) {
2942                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2943             }
2944             else {
2945
2946                 return false;
2947             }
2948         },
2949
2950
2951         releaseObject:function(o)
2952         {
2953
2954             o.conn = null;
2955
2956             o = null;
2957         },
2958
2959         activeX:[
2960         'MSXML2.XMLHTTP.3.0',
2961         'MSXML2.XMLHTTP',
2962         'Microsoft.XMLHTTP'
2963         ]
2964
2965
2966     };
2967 })();/*
2968  * Portions of this file are based on pieces of Yahoo User Interface Library
2969  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2970  * YUI licensed under the BSD License:
2971  * http://developer.yahoo.net/yui/license.txt
2972  * <script type="text/javascript">
2973  *
2974  */
2975
2976 Roo.lib.Region = function(t, r, b, l) {
2977     this.top = t;
2978     this[1] = t;
2979     this.right = r;
2980     this.bottom = b;
2981     this.left = l;
2982     this[0] = l;
2983 };
2984
2985
2986 Roo.lib.Region.prototype = {
2987     contains : function(region) {
2988         return ( region.left >= this.left &&
2989                  region.right <= this.right &&
2990                  region.top >= this.top &&
2991                  region.bottom <= this.bottom    );
2992
2993     },
2994
2995     getArea : function() {
2996         return ( (this.bottom - this.top) * (this.right - this.left) );
2997     },
2998
2999     intersect : function(region) {
3000         var t = Math.max(this.top, region.top);
3001         var r = Math.min(this.right, region.right);
3002         var b = Math.min(this.bottom, region.bottom);
3003         var l = Math.max(this.left, region.left);
3004
3005         if (b >= t && r >= l) {
3006             return new Roo.lib.Region(t, r, b, l);
3007         } else {
3008             return null;
3009         }
3010     },
3011     union : function(region) {
3012         var t = Math.min(this.top, region.top);
3013         var r = Math.max(this.right, region.right);
3014         var b = Math.max(this.bottom, region.bottom);
3015         var l = Math.min(this.left, region.left);
3016
3017         return new Roo.lib.Region(t, r, b, l);
3018     },
3019
3020     adjust : function(t, l, b, r) {
3021         this.top += t;
3022         this.left += l;
3023         this.right += r;
3024         this.bottom += b;
3025         return this;
3026     }
3027 };
3028
3029 Roo.lib.Region.getRegion = function(el) {
3030     var p = Roo.lib.Dom.getXY(el);
3031
3032     var t = p[1];
3033     var r = p[0] + el.offsetWidth;
3034     var b = p[1] + el.offsetHeight;
3035     var l = p[0];
3036
3037     return new Roo.lib.Region(t, r, b, l);
3038 };
3039 /*
3040  * Portions of this file are based on pieces of Yahoo User Interface Library
3041  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3042  * YUI licensed under the BSD License:
3043  * http://developer.yahoo.net/yui/license.txt
3044  * <script type="text/javascript">
3045  *
3046  */
3047 //@@dep Roo.lib.Region
3048
3049
3050 Roo.lib.Point = function(x, y) {
3051     if (x instanceof Array) {
3052         y = x[1];
3053         x = x[0];
3054     }
3055     this.x = this.right = this.left = this[0] = x;
3056     this.y = this.top = this.bottom = this[1] = y;
3057 };
3058
3059 Roo.lib.Point.prototype = new Roo.lib.Region();
3060 /*
3061  * Portions of this file are based on pieces of Yahoo User Interface Library
3062  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3063  * YUI licensed under the BSD License:
3064  * http://developer.yahoo.net/yui/license.txt
3065  * <script type="text/javascript">
3066  *
3067  */
3068  
3069 (function() {   
3070
3071     Roo.lib.Anim = {
3072         scroll : function(el, args, duration, easing, cb, scope) {
3073             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3074         },
3075
3076         motion : function(el, args, duration, easing, cb, scope) {
3077             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3078         },
3079
3080         color : function(el, args, duration, easing, cb, scope) {
3081             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3082         },
3083
3084         run : function(el, args, duration, easing, cb, scope, type) {
3085             type = type || Roo.lib.AnimBase;
3086             if (typeof easing == "string") {
3087                 easing = Roo.lib.Easing[easing];
3088             }
3089             var anim = new type(el, args, duration, easing);
3090             anim.animateX(function() {
3091                 Roo.callback(cb, scope);
3092             });
3093             return anim;
3094         }
3095     };
3096 })();/*
3097  * Portions of this file are based on pieces of Yahoo User Interface Library
3098  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3099  * YUI licensed under the BSD License:
3100  * http://developer.yahoo.net/yui/license.txt
3101  * <script type="text/javascript">
3102  *
3103  */
3104
3105 (function() {    
3106     var libFlyweight;
3107     
3108     function fly(el) {
3109         if (!libFlyweight) {
3110             libFlyweight = new Roo.Element.Flyweight();
3111         }
3112         libFlyweight.dom = el;
3113         return libFlyweight;
3114     }
3115
3116     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3117     
3118    
3119     
3120     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3121         if (el) {
3122             this.init(el, attributes, duration, method);
3123         }
3124     };
3125
3126     Roo.lib.AnimBase.fly = fly;
3127     
3128     
3129     
3130     Roo.lib.AnimBase.prototype = {
3131
3132         toString: function() {
3133             var el = this.getEl();
3134             var id = el.id || el.tagName;
3135             return ("Anim " + id);
3136         },
3137
3138         patterns: {
3139             noNegatives:        /width|height|opacity|padding/i,
3140             offsetAttribute:  /^((width|height)|(top|left))$/,
3141             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3142             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3143         },
3144
3145
3146         doMethod: function(attr, start, end) {
3147             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3148         },
3149
3150
3151         setAttribute: function(attr, val, unit) {
3152             if (this.patterns.noNegatives.test(attr)) {
3153                 val = (val > 0) ? val : 0;
3154             }
3155
3156             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3157         },
3158
3159
3160         getAttribute: function(attr) {
3161             var el = this.getEl();
3162             var val = fly(el).getStyle(attr);
3163
3164             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3165                 return parseFloat(val);
3166             }
3167
3168             var a = this.patterns.offsetAttribute.exec(attr) || [];
3169             var pos = !!( a[3] );
3170             var box = !!( a[2] );
3171
3172
3173             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3174                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3175             } else {
3176                 val = 0;
3177             }
3178
3179             return val;
3180         },
3181
3182
3183         getDefaultUnit: function(attr) {
3184             if (this.patterns.defaultUnit.test(attr)) {
3185                 return 'px';
3186             }
3187
3188             return '';
3189         },
3190
3191         animateX : function(callback, scope) {
3192             var f = function() {
3193                 this.onComplete.removeListener(f);
3194                 if (typeof callback == "function") {
3195                     callback.call(scope || this, this);
3196                 }
3197             };
3198             this.onComplete.addListener(f, this);
3199             this.animate();
3200         },
3201
3202
3203         setRuntimeAttribute: function(attr) {
3204             var start;
3205             var end;
3206             var attributes = this.attributes;
3207
3208             this.runtimeAttributes[attr] = {};
3209
3210             var isset = function(prop) {
3211                 return (typeof prop !== 'undefined');
3212             };
3213
3214             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3215                 return false;
3216             }
3217
3218             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3219
3220
3221             if (isset(attributes[attr]['to'])) {
3222                 end = attributes[attr]['to'];
3223             } else if (isset(attributes[attr]['by'])) {
3224                 if (start.constructor == Array) {
3225                     end = [];
3226                     for (var i = 0, len = start.length; i < len; ++i) {
3227                         end[i] = start[i] + attributes[attr]['by'][i];
3228                     }
3229                 } else {
3230                     end = start + attributes[attr]['by'];
3231                 }
3232             }
3233
3234             this.runtimeAttributes[attr].start = start;
3235             this.runtimeAttributes[attr].end = end;
3236
3237
3238             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3239         },
3240
3241
3242         init: function(el, attributes, duration, method) {
3243
3244             var isAnimated = false;
3245
3246
3247             var startTime = null;
3248
3249
3250             var actualFrames = 0;
3251
3252
3253             el = Roo.getDom(el);
3254
3255
3256             this.attributes = attributes || {};
3257
3258
3259             this.duration = duration || 1;
3260
3261
3262             this.method = method || Roo.lib.Easing.easeNone;
3263
3264
3265             this.useSeconds = true;
3266
3267
3268             this.currentFrame = 0;
3269
3270
3271             this.totalFrames = Roo.lib.AnimMgr.fps;
3272
3273
3274             this.getEl = function() {
3275                 return el;
3276             };
3277
3278
3279             this.isAnimated = function() {
3280                 return isAnimated;
3281             };
3282
3283
3284             this.getStartTime = function() {
3285                 return startTime;
3286             };
3287
3288             this.runtimeAttributes = {};
3289
3290
3291             this.animate = function() {
3292                 if (this.isAnimated()) {
3293                     return false;
3294                 }
3295
3296                 this.currentFrame = 0;
3297
3298                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3299
3300                 Roo.lib.AnimMgr.registerElement(this);
3301             };
3302
3303
3304             this.stop = function(finish) {
3305                 if (finish) {
3306                     this.currentFrame = this.totalFrames;
3307                     this._onTween.fire();
3308                 }
3309                 Roo.lib.AnimMgr.stop(this);
3310             };
3311
3312             var onStart = function() {
3313                 this.onStart.fire();
3314
3315                 this.runtimeAttributes = {};
3316                 for (var attr in this.attributes) {
3317                     this.setRuntimeAttribute(attr);
3318                 }
3319
3320                 isAnimated = true;
3321                 actualFrames = 0;
3322                 startTime = new Date();
3323             };
3324
3325
3326             var onTween = function() {
3327                 var data = {
3328                     duration: new Date() - this.getStartTime(),
3329                     currentFrame: this.currentFrame
3330                 };
3331
3332                 data.toString = function() {
3333                     return (
3334                             'duration: ' + data.duration +
3335                             ', currentFrame: ' + data.currentFrame
3336                             );
3337                 };
3338
3339                 this.onTween.fire(data);
3340
3341                 var runtimeAttributes = this.runtimeAttributes;
3342
3343                 for (var attr in runtimeAttributes) {
3344                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3345                 }
3346
3347                 actualFrames += 1;
3348             };
3349
3350             var onComplete = function() {
3351                 var actual_duration = (new Date() - startTime) / 1000 ;
3352
3353                 var data = {
3354                     duration: actual_duration,
3355                     frames: actualFrames,
3356                     fps: actualFrames / actual_duration
3357                 };
3358
3359                 data.toString = function() {
3360                     return (
3361                             'duration: ' + data.duration +
3362                             ', frames: ' + data.frames +
3363                             ', fps: ' + data.fps
3364                             );
3365                 };
3366
3367                 isAnimated = false;
3368                 actualFrames = 0;
3369                 this.onComplete.fire(data);
3370             };
3371
3372
3373             this._onStart = new Roo.util.Event(this);
3374             this.onStart = new Roo.util.Event(this);
3375             this.onTween = new Roo.util.Event(this);
3376             this._onTween = new Roo.util.Event(this);
3377             this.onComplete = new Roo.util.Event(this);
3378             this._onComplete = new Roo.util.Event(this);
3379             this._onStart.addListener(onStart);
3380             this._onTween.addListener(onTween);
3381             this._onComplete.addListener(onComplete);
3382         }
3383     };
3384 })();
3385 /*
3386  * Portions of this file are based on pieces of Yahoo User Interface Library
3387  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3388  * YUI licensed under the BSD License:
3389  * http://developer.yahoo.net/yui/license.txt
3390  * <script type="text/javascript">
3391  *
3392  */
3393
3394 Roo.lib.AnimMgr = new function() {
3395
3396         var thread = null;
3397
3398
3399         var queue = [];
3400
3401
3402         var tweenCount = 0;
3403
3404
3405         this.fps = 1000;
3406
3407
3408         this.delay = 1;
3409
3410
3411         this.registerElement = function(tween) {
3412             queue[queue.length] = tween;
3413             tweenCount += 1;
3414             tween._onStart.fire();
3415             this.start();
3416         };
3417
3418
3419         this.unRegister = function(tween, index) {
3420             tween._onComplete.fire();
3421             index = index || getIndex(tween);
3422             if (index != -1) {
3423                 queue.splice(index, 1);
3424             }
3425
3426             tweenCount -= 1;
3427             if (tweenCount <= 0) {
3428                 this.stop();
3429             }
3430         };
3431
3432
3433         this.start = function() {
3434             if (thread === null) {
3435                 thread = setInterval(this.run, this.delay);
3436             }
3437         };
3438
3439
3440         this.stop = function(tween) {
3441             if (!tween) {
3442                 clearInterval(thread);
3443
3444                 for (var i = 0, len = queue.length; i < len; ++i) {
3445                     if (queue[0].isAnimated()) {
3446                         this.unRegister(queue[0], 0);
3447                     }
3448                 }
3449
3450                 queue = [];
3451                 thread = null;
3452                 tweenCount = 0;
3453             }
3454             else {
3455                 this.unRegister(tween);
3456             }
3457         };
3458
3459
3460         this.run = function() {
3461             for (var i = 0, len = queue.length; i < len; ++i) {
3462                 var tween = queue[i];
3463                 if (!tween || !tween.isAnimated()) {
3464                     continue;
3465                 }
3466
3467                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3468                 {
3469                     tween.currentFrame += 1;
3470
3471                     if (tween.useSeconds) {
3472                         correctFrame(tween);
3473                     }
3474                     tween._onTween.fire();
3475                 }
3476                 else {
3477                     Roo.lib.AnimMgr.stop(tween, i);
3478                 }
3479             }
3480         };
3481
3482         var getIndex = function(anim) {
3483             for (var i = 0, len = queue.length; i < len; ++i) {
3484                 if (queue[i] == anim) {
3485                     return i;
3486                 }
3487             }
3488             return -1;
3489         };
3490
3491
3492         var correctFrame = function(tween) {
3493             var frames = tween.totalFrames;
3494             var frame = tween.currentFrame;
3495             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3496             var elapsed = (new Date() - tween.getStartTime());
3497             var tweak = 0;
3498
3499             if (elapsed < tween.duration * 1000) {
3500                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3501             } else {
3502                 tweak = frames - (frame + 1);
3503             }
3504             if (tweak > 0 && isFinite(tweak)) {
3505                 if (tween.currentFrame + tweak >= frames) {
3506                     tweak = frames - (frame + 1);
3507                 }
3508
3509                 tween.currentFrame += tweak;
3510             }
3511         };
3512     };/*
3513  * Portions of this file are based on pieces of Yahoo User Interface Library
3514  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3515  * YUI licensed under the BSD License:
3516  * http://developer.yahoo.net/yui/license.txt
3517  * <script type="text/javascript">
3518  *
3519  */
3520 Roo.lib.Bezier = new function() {
3521
3522         this.getPosition = function(points, t) {
3523             var n = points.length;
3524             var tmp = [];
3525
3526             for (var i = 0; i < n; ++i) {
3527                 tmp[i] = [points[i][0], points[i][1]];
3528             }
3529
3530             for (var j = 1; j < n; ++j) {
3531                 for (i = 0; i < n - j; ++i) {
3532                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3533                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3534                 }
3535             }
3536
3537             return [ tmp[0][0], tmp[0][1] ];
3538
3539         };
3540     };/*
3541  * Portions of this file are based on pieces of Yahoo User Interface Library
3542  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3543  * YUI licensed under the BSD License:
3544  * http://developer.yahoo.net/yui/license.txt
3545  * <script type="text/javascript">
3546  *
3547  */
3548 (function() {
3549
3550     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3551         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3552     };
3553
3554     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3555
3556     var fly = Roo.lib.AnimBase.fly;
3557     var Y = Roo.lib;
3558     var superclass = Y.ColorAnim.superclass;
3559     var proto = Y.ColorAnim.prototype;
3560
3561     proto.toString = function() {
3562         var el = this.getEl();
3563         var id = el.id || el.tagName;
3564         return ("ColorAnim " + id);
3565     };
3566
3567     proto.patterns.color = /color$/i;
3568     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3569     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3570     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3571     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3572
3573
3574     proto.parseColor = function(s) {
3575         if (s.length == 3) {
3576             return s;
3577         }
3578
3579         var c = this.patterns.hex.exec(s);
3580         if (c && c.length == 4) {
3581             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3582         }
3583
3584         c = this.patterns.rgb.exec(s);
3585         if (c && c.length == 4) {
3586             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3587         }
3588
3589         c = this.patterns.hex3.exec(s);
3590         if (c && c.length == 4) {
3591             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3592         }
3593
3594         return null;
3595     };
3596     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3597     proto.getAttribute = function(attr) {
3598         var el = this.getEl();
3599         if (this.patterns.color.test(attr)) {
3600             var val = fly(el).getStyle(attr);
3601
3602             if (this.patterns.transparent.test(val)) {
3603                 var parent = el.parentNode;
3604                 val = fly(parent).getStyle(attr);
3605
3606                 while (parent && this.patterns.transparent.test(val)) {
3607                     parent = parent.parentNode;
3608                     val = fly(parent).getStyle(attr);
3609                     if (parent.tagName.toUpperCase() == 'HTML') {
3610                         val = '#fff';
3611                     }
3612                 }
3613             }
3614         } else {
3615             val = superclass.getAttribute.call(this, attr);
3616         }
3617
3618         return val;
3619     };
3620     proto.getAttribute = function(attr) {
3621         var el = this.getEl();
3622         if (this.patterns.color.test(attr)) {
3623             var val = fly(el).getStyle(attr);
3624
3625             if (this.patterns.transparent.test(val)) {
3626                 var parent = el.parentNode;
3627                 val = fly(parent).getStyle(attr);
3628
3629                 while (parent && this.patterns.transparent.test(val)) {
3630                     parent = parent.parentNode;
3631                     val = fly(parent).getStyle(attr);
3632                     if (parent.tagName.toUpperCase() == 'HTML') {
3633                         val = '#fff';
3634                     }
3635                 }
3636             }
3637         } else {
3638             val = superclass.getAttribute.call(this, attr);
3639         }
3640
3641         return val;
3642     };
3643
3644     proto.doMethod = function(attr, start, end) {
3645         var val;
3646
3647         if (this.patterns.color.test(attr)) {
3648             val = [];
3649             for (var i = 0, len = start.length; i < len; ++i) {
3650                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3651             }
3652
3653             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3654         }
3655         else {
3656             val = superclass.doMethod.call(this, attr, start, end);
3657         }
3658
3659         return val;
3660     };
3661
3662     proto.setRuntimeAttribute = function(attr) {
3663         superclass.setRuntimeAttribute.call(this, attr);
3664
3665         if (this.patterns.color.test(attr)) {
3666             var attributes = this.attributes;
3667             var start = this.parseColor(this.runtimeAttributes[attr].start);
3668             var end = this.parseColor(this.runtimeAttributes[attr].end);
3669
3670             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3671                 end = this.parseColor(attributes[attr].by);
3672
3673                 for (var i = 0, len = start.length; i < len; ++i) {
3674                     end[i] = start[i] + end[i];
3675                 }
3676             }
3677
3678             this.runtimeAttributes[attr].start = start;
3679             this.runtimeAttributes[attr].end = end;
3680         }
3681     };
3682 })();
3683
3684 /*
3685  * Portions of this file are based on pieces of Yahoo User Interface Library
3686  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3687  * YUI licensed under the BSD License:
3688  * http://developer.yahoo.net/yui/license.txt
3689  * <script type="text/javascript">
3690  *
3691  */
3692 Roo.lib.Easing = {
3693
3694
3695     easeNone: function (t, b, c, d) {
3696         return c * t / d + b;
3697     },
3698
3699
3700     easeIn: function (t, b, c, d) {
3701         return c * (t /= d) * t + b;
3702     },
3703
3704
3705     easeOut: function (t, b, c, d) {
3706         return -c * (t /= d) * (t - 2) + b;
3707     },
3708
3709
3710     easeBoth: function (t, b, c, d) {
3711         if ((t /= d / 2) < 1) {
3712             return c / 2 * t * t + b;
3713         }
3714
3715         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3716     },
3717
3718
3719     easeInStrong: function (t, b, c, d) {
3720         return c * (t /= d) * t * t * t + b;
3721     },
3722
3723
3724     easeOutStrong: function (t, b, c, d) {
3725         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3726     },
3727
3728
3729     easeBothStrong: function (t, b, c, d) {
3730         if ((t /= d / 2) < 1) {
3731             return c / 2 * t * t * t * t + b;
3732         }
3733
3734         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3735     },
3736
3737
3738
3739     elasticIn: function (t, b, c, d, a, p) {
3740         if (t == 0) {
3741             return b;
3742         }
3743         if ((t /= d) == 1) {
3744             return b + c;
3745         }
3746         if (!p) {
3747             p = d * .3;
3748         }
3749
3750         if (!a || a < Math.abs(c)) {
3751             a = c;
3752             var s = p / 4;
3753         }
3754         else {
3755             var s = p / (2 * Math.PI) * Math.asin(c / a);
3756         }
3757
3758         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3759     },
3760
3761
3762     elasticOut: function (t, b, c, d, a, p) {
3763         if (t == 0) {
3764             return b;
3765         }
3766         if ((t /= d) == 1) {
3767             return b + c;
3768         }
3769         if (!p) {
3770             p = d * .3;
3771         }
3772
3773         if (!a || a < Math.abs(c)) {
3774             a = c;
3775             var s = p / 4;
3776         }
3777         else {
3778             var s = p / (2 * Math.PI) * Math.asin(c / a);
3779         }
3780
3781         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3782     },
3783
3784
3785     elasticBoth: function (t, b, c, d, a, p) {
3786         if (t == 0) {
3787             return b;
3788         }
3789
3790         if ((t /= d / 2) == 2) {
3791             return b + c;
3792         }
3793
3794         if (!p) {
3795             p = d * (.3 * 1.5);
3796         }
3797
3798         if (!a || a < Math.abs(c)) {
3799             a = c;
3800             var s = p / 4;
3801         }
3802         else {
3803             var s = p / (2 * Math.PI) * Math.asin(c / a);
3804         }
3805
3806         if (t < 1) {
3807             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3808                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3809         }
3810         return a * Math.pow(2, -10 * (t -= 1)) *
3811                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3812     },
3813
3814
3815
3816     backIn: function (t, b, c, d, s) {
3817         if (typeof s == 'undefined') {
3818             s = 1.70158;
3819         }
3820         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3821     },
3822
3823
3824     backOut: function (t, b, c, d, s) {
3825         if (typeof s == 'undefined') {
3826             s = 1.70158;
3827         }
3828         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3829     },
3830
3831
3832     backBoth: function (t, b, c, d, s) {
3833         if (typeof s == 'undefined') {
3834             s = 1.70158;
3835         }
3836
3837         if ((t /= d / 2 ) < 1) {
3838             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3839         }
3840         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3841     },
3842
3843
3844     bounceIn: function (t, b, c, d) {
3845         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3846     },
3847
3848
3849     bounceOut: function (t, b, c, d) {
3850         if ((t /= d) < (1 / 2.75)) {
3851             return c * (7.5625 * t * t) + b;
3852         } else if (t < (2 / 2.75)) {
3853             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3854         } else if (t < (2.5 / 2.75)) {
3855             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3856         }
3857         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3858     },
3859
3860
3861     bounceBoth: function (t, b, c, d) {
3862         if (t < d / 2) {
3863             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3864         }
3865         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3866     }
3867 };/*
3868  * Portions of this file are based on pieces of Yahoo User Interface Library
3869  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3870  * YUI licensed under the BSD License:
3871  * http://developer.yahoo.net/yui/license.txt
3872  * <script type="text/javascript">
3873  *
3874  */
3875     (function() {
3876         Roo.lib.Motion = function(el, attributes, duration, method) {
3877             if (el) {
3878                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3879             }
3880         };
3881
3882         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3883
3884
3885         var Y = Roo.lib;
3886         var superclass = Y.Motion.superclass;
3887         var proto = Y.Motion.prototype;
3888
3889         proto.toString = function() {
3890             var el = this.getEl();
3891             var id = el.id || el.tagName;
3892             return ("Motion " + id);
3893         };
3894
3895         proto.patterns.points = /^points$/i;
3896
3897         proto.setAttribute = function(attr, val, unit) {
3898             if (this.patterns.points.test(attr)) {
3899                 unit = unit || 'px';
3900                 superclass.setAttribute.call(this, 'left', val[0], unit);
3901                 superclass.setAttribute.call(this, 'top', val[1], unit);
3902             } else {
3903                 superclass.setAttribute.call(this, attr, val, unit);
3904             }
3905         };
3906
3907         proto.getAttribute = function(attr) {
3908             if (this.patterns.points.test(attr)) {
3909                 var val = [
3910                         superclass.getAttribute.call(this, 'left'),
3911                         superclass.getAttribute.call(this, 'top')
3912                         ];
3913             } else {
3914                 val = superclass.getAttribute.call(this, attr);
3915             }
3916
3917             return val;
3918         };
3919
3920         proto.doMethod = function(attr, start, end) {
3921             var val = null;
3922
3923             if (this.patterns.points.test(attr)) {
3924                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3925                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3926             } else {
3927                 val = superclass.doMethod.call(this, attr, start, end);
3928             }
3929             return val;
3930         };
3931
3932         proto.setRuntimeAttribute = function(attr) {
3933             if (this.patterns.points.test(attr)) {
3934                 var el = this.getEl();
3935                 var attributes = this.attributes;
3936                 var start;
3937                 var control = attributes['points']['control'] || [];
3938                 var end;
3939                 var i, len;
3940
3941                 if (control.length > 0 && !(control[0] instanceof Array)) {
3942                     control = [control];
3943                 } else {
3944                     var tmp = [];
3945                     for (i = 0,len = control.length; i < len; ++i) {
3946                         tmp[i] = control[i];
3947                     }
3948                     control = tmp;
3949                 }
3950
3951                 Roo.fly(el).position();
3952
3953                 if (isset(attributes['points']['from'])) {
3954                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3955                 }
3956                 else {
3957                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3958                 }
3959
3960                 start = this.getAttribute('points');
3961
3962
3963                 if (isset(attributes['points']['to'])) {
3964                     end = translateValues.call(this, attributes['points']['to'], start);
3965
3966                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3967                     for (i = 0,len = control.length; i < len; ++i) {
3968                         control[i] = translateValues.call(this, control[i], start);
3969                     }
3970
3971
3972                 } else if (isset(attributes['points']['by'])) {
3973                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3974
3975                     for (i = 0,len = control.length; i < len; ++i) {
3976                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3977                     }
3978                 }
3979
3980                 this.runtimeAttributes[attr] = [start];
3981
3982                 if (control.length > 0) {
3983                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3984                 }
3985
3986                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3987             }
3988             else {
3989                 superclass.setRuntimeAttribute.call(this, attr);
3990             }
3991         };
3992
3993         var translateValues = function(val, start) {
3994             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3995             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3996
3997             return val;
3998         };
3999
4000         var isset = function(prop) {
4001             return (typeof prop !== 'undefined');
4002         };
4003     })();
4004 /*
4005  * Portions of this file are based on pieces of Yahoo User Interface Library
4006  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4007  * YUI licensed under the BSD License:
4008  * http://developer.yahoo.net/yui/license.txt
4009  * <script type="text/javascript">
4010  *
4011  */
4012     (function() {
4013         Roo.lib.Scroll = function(el, attributes, duration, method) {
4014             if (el) {
4015                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4016             }
4017         };
4018
4019         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4020
4021
4022         var Y = Roo.lib;
4023         var superclass = Y.Scroll.superclass;
4024         var proto = Y.Scroll.prototype;
4025
4026         proto.toString = function() {
4027             var el = this.getEl();
4028             var id = el.id || el.tagName;
4029             return ("Scroll " + id);
4030         };
4031
4032         proto.doMethod = function(attr, start, end) {
4033             var val = null;
4034
4035             if (attr == 'scroll') {
4036                 val = [
4037                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4038                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4039                         ];
4040
4041             } else {
4042                 val = superclass.doMethod.call(this, attr, start, end);
4043             }
4044             return val;
4045         };
4046
4047         proto.getAttribute = function(attr) {
4048             var val = null;
4049             var el = this.getEl();
4050
4051             if (attr == 'scroll') {
4052                 val = [ el.scrollLeft, el.scrollTop ];
4053             } else {
4054                 val = superclass.getAttribute.call(this, attr);
4055             }
4056
4057             return val;
4058         };
4059
4060         proto.setAttribute = function(attr, val, unit) {
4061             var el = this.getEl();
4062
4063             if (attr == 'scroll') {
4064                 el.scrollLeft = val[0];
4065                 el.scrollTop = val[1];
4066             } else {
4067                 superclass.setAttribute.call(this, attr, val, unit);
4068             }
4069         };
4070     })();
4071 /*
4072  * Based on:
4073  * Ext JS Library 1.1.1
4074  * Copyright(c) 2006-2007, Ext JS, LLC.
4075  *
4076  * Originally Released Under LGPL - original licence link has changed is not relivant.
4077  *
4078  * Fork - LGPL
4079  * <script type="text/javascript">
4080  */
4081
4082
4083 // nasty IE9 hack - what a pile of crap that is..
4084
4085  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4086     Range.prototype.createContextualFragment = function (html) {
4087         var doc = window.document;
4088         var container = doc.createElement("div");
4089         container.innerHTML = html;
4090         var frag = doc.createDocumentFragment(), n;
4091         while ((n = container.firstChild)) {
4092             frag.appendChild(n);
4093         }
4094         return frag;
4095     };
4096 }
4097
4098 /**
4099  * @class Roo.DomHelper
4100  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4101  * 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>.
4102  * @singleton
4103  */
4104 Roo.DomHelper = function(){
4105     var tempTableEl = null;
4106     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4107     var tableRe = /^table|tbody|tr|td$/i;
4108     var xmlns = {};
4109     // build as innerHTML where available
4110     /** @ignore */
4111     var createHtml = function(o){
4112         if(typeof o == 'string'){
4113             return o;
4114         }
4115         var b = "";
4116         if(!o.tag){
4117             o.tag = "div";
4118         }
4119         b += "<" + o.tag;
4120         for(var attr in o){
4121             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4122             if(attr == "style"){
4123                 var s = o["style"];
4124                 if(typeof s == "function"){
4125                     s = s.call();
4126                 }
4127                 if(typeof s == "string"){
4128                     b += ' style="' + s + '"';
4129                 }else if(typeof s == "object"){
4130                     b += ' style="';
4131                     for(var key in s){
4132                         if(typeof s[key] != "function"){
4133                             b += key + ":" + s[key] + ";";
4134                         }
4135                     }
4136                     b += '"';
4137                 }
4138             }else{
4139                 if(attr == "cls"){
4140                     b += ' class="' + o["cls"] + '"';
4141                 }else if(attr == "htmlFor"){
4142                     b += ' for="' + o["htmlFor"] + '"';
4143                 }else{
4144                     b += " " + attr + '="' + o[attr] + '"';
4145                 }
4146             }
4147         }
4148         if(emptyTags.test(o.tag)){
4149             b += "/>";
4150         }else{
4151             b += ">";
4152             var cn = o.children || o.cn;
4153             if(cn){
4154                 //http://bugs.kde.org/show_bug.cgi?id=71506
4155                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4156                     for(var i = 0, len = cn.length; i < len; i++) {
4157                         b += createHtml(cn[i], b);
4158                     }
4159                 }else{
4160                     b += createHtml(cn, b);
4161                 }
4162             }
4163             if(o.html){
4164                 b += o.html;
4165             }
4166             b += "</" + o.tag + ">";
4167         }
4168         return b;
4169     };
4170
4171     // build as dom
4172     /** @ignore */
4173     var createDom = function(o, parentNode){
4174          
4175         // defininition craeted..
4176         var ns = false;
4177         if (o.ns && o.ns != 'html') {
4178                
4179             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4180                 xmlns[o.ns] = o.xmlns;
4181                 ns = o.xmlns;
4182             }
4183             if (typeof(xmlns[o.ns]) == 'undefined') {
4184                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4185             }
4186             ns = xmlns[o.ns];
4187         }
4188         
4189         
4190         if (typeof(o) == 'string') {
4191             return parentNode.appendChild(document.createTextNode(o));
4192         }
4193         o.tag = o.tag || div;
4194         if (o.ns && Roo.isIE) {
4195             ns = false;
4196             o.tag = o.ns + ':' + o.tag;
4197             
4198         }
4199         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4200         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4201         for(var attr in o){
4202             
4203             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4204                     attr == "style" || typeof o[attr] == "function") continue;
4205                     
4206             if(attr=="cls" && Roo.isIE){
4207                 el.className = o["cls"];
4208             }else{
4209                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4210                 else el[attr] = o[attr];
4211             }
4212         }
4213         Roo.DomHelper.applyStyles(el, o.style);
4214         var cn = o.children || o.cn;
4215         if(cn){
4216             //http://bugs.kde.org/show_bug.cgi?id=71506
4217              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4218                 for(var i = 0, len = cn.length; i < len; i++) {
4219                     createDom(cn[i], el);
4220                 }
4221             }else{
4222                 createDom(cn, el);
4223             }
4224         }
4225         if(o.html){
4226             el.innerHTML = o.html;
4227         }
4228         if(parentNode){
4229            parentNode.appendChild(el);
4230         }
4231         return el;
4232     };
4233
4234     var ieTable = function(depth, s, h, e){
4235         tempTableEl.innerHTML = [s, h, e].join('');
4236         var i = -1, el = tempTableEl;
4237         while(++i < depth){
4238             el = el.firstChild;
4239         }
4240         return el;
4241     };
4242
4243     // kill repeat to save bytes
4244     var ts = '<table>',
4245         te = '</table>',
4246         tbs = ts+'<tbody>',
4247         tbe = '</tbody>'+te,
4248         trs = tbs + '<tr>',
4249         tre = '</tr>'+tbe;
4250
4251     /**
4252      * @ignore
4253      * Nasty code for IE's broken table implementation
4254      */
4255     var insertIntoTable = function(tag, where, el, html){
4256         if(!tempTableEl){
4257             tempTableEl = document.createElement('div');
4258         }
4259         var node;
4260         var before = null;
4261         if(tag == 'td'){
4262             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4263                 return;
4264             }
4265             if(where == 'beforebegin'){
4266                 before = el;
4267                 el = el.parentNode;
4268             } else{
4269                 before = el.nextSibling;
4270                 el = el.parentNode;
4271             }
4272             node = ieTable(4, trs, html, tre);
4273         }
4274         else if(tag == 'tr'){
4275             if(where == 'beforebegin'){
4276                 before = el;
4277                 el = el.parentNode;
4278                 node = ieTable(3, tbs, html, tbe);
4279             } else if(where == 'afterend'){
4280                 before = el.nextSibling;
4281                 el = el.parentNode;
4282                 node = ieTable(3, tbs, html, tbe);
4283             } else{ // INTO a TR
4284                 if(where == 'afterbegin'){
4285                     before = el.firstChild;
4286                 }
4287                 node = ieTable(4, trs, html, tre);
4288             }
4289         } else if(tag == 'tbody'){
4290             if(where == 'beforebegin'){
4291                 before = el;
4292                 el = el.parentNode;
4293                 node = ieTable(2, ts, html, te);
4294             } else if(where == 'afterend'){
4295                 before = el.nextSibling;
4296                 el = el.parentNode;
4297                 node = ieTable(2, ts, html, te);
4298             } else{
4299                 if(where == 'afterbegin'){
4300                     before = el.firstChild;
4301                 }
4302                 node = ieTable(3, tbs, html, tbe);
4303             }
4304         } else{ // TABLE
4305             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4306                 return;
4307             }
4308             if(where == 'afterbegin'){
4309                 before = el.firstChild;
4310             }
4311             node = ieTable(2, ts, html, te);
4312         }
4313         el.insertBefore(node, before);
4314         return node;
4315     };
4316
4317     return {
4318     /** True to force the use of DOM instead of html fragments @type Boolean */
4319     useDom : false,
4320
4321     /**
4322      * Returns the markup for the passed Element(s) config
4323      * @param {Object} o The Dom object spec (and children)
4324      * @return {String}
4325      */
4326     markup : function(o){
4327         return createHtml(o);
4328     },
4329
4330     /**
4331      * Applies a style specification to an element
4332      * @param {String/HTMLElement} el The element to apply styles to
4333      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4334      * a function which returns such a specification.
4335      */
4336     applyStyles : function(el, styles){
4337         if(styles){
4338            el = Roo.fly(el);
4339            if(typeof styles == "string"){
4340                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4341                var matches;
4342                while ((matches = re.exec(styles)) != null){
4343                    el.setStyle(matches[1], matches[2]);
4344                }
4345            }else if (typeof styles == "object"){
4346                for (var style in styles){
4347                   el.setStyle(style, styles[style]);
4348                }
4349            }else if (typeof styles == "function"){
4350                 Roo.DomHelper.applyStyles(el, styles.call());
4351            }
4352         }
4353     },
4354
4355     /**
4356      * Inserts an HTML fragment into the Dom
4357      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4358      * @param {HTMLElement} el The context element
4359      * @param {String} html The HTML fragmenet
4360      * @return {HTMLElement} The new node
4361      */
4362     insertHtml : function(where, el, html){
4363         where = where.toLowerCase();
4364         if(el.insertAdjacentHTML){
4365             if(tableRe.test(el.tagName)){
4366                 var rs;
4367                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4368                     return rs;
4369                 }
4370             }
4371             switch(where){
4372                 case "beforebegin":
4373                     el.insertAdjacentHTML('BeforeBegin', html);
4374                     return el.previousSibling;
4375                 case "afterbegin":
4376                     el.insertAdjacentHTML('AfterBegin', html);
4377                     return el.firstChild;
4378                 case "beforeend":
4379                     el.insertAdjacentHTML('BeforeEnd', html);
4380                     return el.lastChild;
4381                 case "afterend":
4382                     el.insertAdjacentHTML('AfterEnd', html);
4383                     return el.nextSibling;
4384             }
4385             throw 'Illegal insertion point -> "' + where + '"';
4386         }
4387         var range = el.ownerDocument.createRange();
4388         var frag;
4389         switch(where){
4390              case "beforebegin":
4391                 range.setStartBefore(el);
4392                 frag = range.createContextualFragment(html);
4393                 el.parentNode.insertBefore(frag, el);
4394                 return el.previousSibling;
4395              case "afterbegin":
4396                 if(el.firstChild){
4397                     range.setStartBefore(el.firstChild);
4398                     frag = range.createContextualFragment(html);
4399                     el.insertBefore(frag, el.firstChild);
4400                     return el.firstChild;
4401                 }else{
4402                     el.innerHTML = html;
4403                     return el.firstChild;
4404                 }
4405             case "beforeend":
4406                 if(el.lastChild){
4407                     range.setStartAfter(el.lastChild);
4408                     frag = range.createContextualFragment(html);
4409                     el.appendChild(frag);
4410                     return el.lastChild;
4411                 }else{
4412                     el.innerHTML = html;
4413                     return el.lastChild;
4414                 }
4415             case "afterend":
4416                 range.setStartAfter(el);
4417                 frag = range.createContextualFragment(html);
4418                 el.parentNode.insertBefore(frag, el.nextSibling);
4419                 return el.nextSibling;
4420             }
4421             throw 'Illegal insertion point -> "' + where + '"';
4422     },
4423
4424     /**
4425      * Creates new Dom element(s) and inserts them before el
4426      * @param {String/HTMLElement/Element} el The context element
4427      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4428      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4429      * @return {HTMLElement/Roo.Element} The new node
4430      */
4431     insertBefore : function(el, o, returnElement){
4432         return this.doInsert(el, o, returnElement, "beforeBegin");
4433     },
4434
4435     /**
4436      * Creates new Dom element(s) and inserts them after el
4437      * @param {String/HTMLElement/Element} el The context element
4438      * @param {Object} o The Dom object spec (and children)
4439      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4440      * @return {HTMLElement/Roo.Element} The new node
4441      */
4442     insertAfter : function(el, o, returnElement){
4443         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4444     },
4445
4446     /**
4447      * Creates new Dom element(s) and inserts them as the first child of el
4448      * @param {String/HTMLElement/Element} el The context element
4449      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4450      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4451      * @return {HTMLElement/Roo.Element} The new node
4452      */
4453     insertFirst : function(el, o, returnElement){
4454         return this.doInsert(el, o, returnElement, "afterBegin");
4455     },
4456
4457     // private
4458     doInsert : function(el, o, returnElement, pos, sibling){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml(pos, el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and appends them to el
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     append : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         var newNode;
4481         if(this.useDom || o.ns){
4482             newNode = createDom(o, null);
4483             el.appendChild(newNode);
4484         }else{
4485             var html = createHtml(o);
4486             newNode = this.insertHtml("beforeEnd", el, html);
4487         }
4488         return returnElement ? Roo.get(newNode, true) : newNode;
4489     },
4490
4491     /**
4492      * Creates new Dom element(s) and overwrites the contents of el with them
4493      * @param {String/HTMLElement/Element} el The context element
4494      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4495      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4496      * @return {HTMLElement/Roo.Element} The new node
4497      */
4498     overwrite : function(el, o, returnElement){
4499         el = Roo.getDom(el);
4500         if (o.ns) {
4501           
4502             while (el.childNodes.length) {
4503                 el.removeChild(el.firstChild);
4504             }
4505             createDom(o, el);
4506         } else {
4507             el.innerHTML = createHtml(o);   
4508         }
4509         
4510         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4511     },
4512
4513     /**
4514      * Creates a new Roo.DomHelper.Template from the Dom object spec
4515      * @param {Object} o The Dom object spec (and children)
4516      * @return {Roo.DomHelper.Template} The new template
4517      */
4518     createTemplate : function(o){
4519         var html = createHtml(o);
4520         return new Roo.Template(html);
4521     }
4522     };
4523 }();
4524 /*
4525  * Based on:
4526  * Ext JS Library 1.1.1
4527  * Copyright(c) 2006-2007, Ext JS, LLC.
4528  *
4529  * Originally Released Under LGPL - original licence link has changed is not relivant.
4530  *
4531  * Fork - LGPL
4532  * <script type="text/javascript">
4533  */
4534  
4535 /**
4536 * @class Roo.Template
4537 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4538 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4539 * Usage:
4540 <pre><code>
4541 var t = new Roo.Template({
4542     html :  '&lt;div name="{id}"&gt;' + 
4543         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4544         '&lt;/div&gt;',
4545     myformat: function (value, allValues) {
4546         return 'XX' + value;
4547     }
4548 });
4549 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4550 </code></pre>
4551 * For more information see this blog post with examples:
4552 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4553      - Create Elements using DOM, HTML fragments and Templates</a>. 
4554 * @constructor
4555 * @param {Object} cfg - Configuration object.
4556 */
4557 Roo.Template = function(cfg){
4558     // BC!
4559     if(cfg instanceof Array){
4560         cfg = cfg.join("");
4561     }else if(arguments.length > 1){
4562         cfg = Array.prototype.join.call(arguments, "");
4563     }
4564     
4565     
4566     if (typeof(cfg) == 'object') {
4567         Roo.apply(this,cfg)
4568     } else {
4569         // bc
4570         this.html = cfg;
4571     }
4572     if (this.url) {
4573         this.load();
4574     }
4575     
4576 };
4577 Roo.Template.prototype = {
4578     
4579     /**
4580      * @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..
4581      *                    it should be fixed so that template is observable...
4582      */
4583     url : false,
4584     /**
4585      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4586      */
4587     html : '',
4588     /**
4589      * Returns an HTML fragment of this template with the specified values applied.
4590      * @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'})
4591      * @return {String} The HTML fragment
4592      */
4593     applyTemplate : function(values){
4594         try {
4595            
4596             if(this.compiled){
4597                 return this.compiled(values);
4598             }
4599             var useF = this.disableFormats !== true;
4600             var fm = Roo.util.Format, tpl = this;
4601             var fn = function(m, name, format, args){
4602                 if(format && useF){
4603                     if(format.substr(0, 5) == "this."){
4604                         return tpl.call(format.substr(5), values[name], values);
4605                     }else{
4606                         if(args){
4607                             // quoted values are required for strings in compiled templates, 
4608                             // but for non compiled we need to strip them
4609                             // quoted reversed for jsmin
4610                             var re = /^\s*['"](.*)["']\s*$/;
4611                             args = args.split(',');
4612                             for(var i = 0, len = args.length; i < len; i++){
4613                                 args[i] = args[i].replace(re, "$1");
4614                             }
4615                             args = [values[name]].concat(args);
4616                         }else{
4617                             args = [values[name]];
4618                         }
4619                         return fm[format].apply(fm, args);
4620                     }
4621                 }else{
4622                     return values[name] !== undefined ? values[name] : "";
4623                 }
4624             };
4625             return this.html.replace(this.re, fn);
4626         } catch (e) {
4627             Roo.log(e);
4628             throw e;
4629         }
4630          
4631     },
4632     
4633     loading : false,
4634       
4635     load : function ()
4636     {
4637          
4638         if (this.loading) {
4639             return;
4640         }
4641         var _t = this;
4642         
4643         this.loading = true;
4644         this.compiled = false;
4645         
4646         var cx = new Roo.data.Connection();
4647         cx.request({
4648             url : this.url,
4649             method : 'GET',
4650             success : function (response) {
4651                 _t.loading = false;
4652                 _t.html = response.responseText;
4653                 _t.url = false;
4654                 _t.compile();
4655              },
4656             failure : function(response) {
4657                 Roo.log("Template failed to load from " + _t.url);
4658                 _t.loading = false;
4659             }
4660         });
4661     },
4662
4663     /**
4664      * Sets the HTML used as the template and optionally compiles it.
4665      * @param {String} html
4666      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4667      * @return {Roo.Template} this
4668      */
4669     set : function(html, compile){
4670         this.html = html;
4671         this.compiled = null;
4672         if(compile){
4673             this.compile();
4674         }
4675         return this;
4676     },
4677     
4678     /**
4679      * True to disable format functions (defaults to false)
4680      * @type Boolean
4681      */
4682     disableFormats : false,
4683     
4684     /**
4685     * The regular expression used to match template variables 
4686     * @type RegExp
4687     * @property 
4688     */
4689     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4690     
4691     /**
4692      * Compiles the template into an internal function, eliminating the RegEx overhead.
4693      * @return {Roo.Template} this
4694      */
4695     compile : function(){
4696         var fm = Roo.util.Format;
4697         var useF = this.disableFormats !== true;
4698         var sep = Roo.isGecko ? "+" : ",";
4699         var fn = function(m, name, format, args){
4700             if(format && useF){
4701                 args = args ? ',' + args : "";
4702                 if(format.substr(0, 5) != "this."){
4703                     format = "fm." + format + '(';
4704                 }else{
4705                     format = 'this.call("'+ format.substr(5) + '", ';
4706                     args = ", values";
4707                 }
4708             }else{
4709                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4710             }
4711             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4712         };
4713         var body;
4714         // branched to use + in gecko and [].join() in others
4715         if(Roo.isGecko){
4716             body = "this.compiled = function(values){ return '" +
4717                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4718                     "';};";
4719         }else{
4720             body = ["this.compiled = function(values){ return ['"];
4721             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4722             body.push("'].join('');};");
4723             body = body.join('');
4724         }
4725         /**
4726          * eval:var:values
4727          * eval:var:fm
4728          */
4729         eval(body);
4730         return this;
4731     },
4732     
4733     // private function used to call members
4734     call : function(fnName, value, allValues){
4735         return this[fnName](value, allValues);
4736     },
4737     
4738     /**
4739      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4740      * @param {String/HTMLElement/Roo.Element} el The context element
4741      * @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'})
4742      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4743      * @return {HTMLElement/Roo.Element} The new node or Element
4744      */
4745     insertFirst: function(el, values, returnElement){
4746         return this.doInsert('afterBegin', el, values, returnElement);
4747     },
4748
4749     /**
4750      * Applies the supplied values to the template and inserts the new node(s) before el.
4751      * @param {String/HTMLElement/Roo.Element} el The context element
4752      * @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'})
4753      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4754      * @return {HTMLElement/Roo.Element} The new node or Element
4755      */
4756     insertBefore: function(el, values, returnElement){
4757         return this.doInsert('beforeBegin', el, values, returnElement);
4758     },
4759
4760     /**
4761      * Applies the supplied values to the template and inserts the new node(s) after el.
4762      * @param {String/HTMLElement/Roo.Element} el The context element
4763      * @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'})
4764      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4765      * @return {HTMLElement/Roo.Element} The new node or Element
4766      */
4767     insertAfter : function(el, values, returnElement){
4768         return this.doInsert('afterEnd', el, values, returnElement);
4769     },
4770     
4771     /**
4772      * Applies the supplied values to the template and appends the new node(s) to el.
4773      * @param {String/HTMLElement/Roo.Element} el The context element
4774      * @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'})
4775      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4776      * @return {HTMLElement/Roo.Element} The new node or Element
4777      */
4778     append : function(el, values, returnElement){
4779         return this.doInsert('beforeEnd', el, values, returnElement);
4780     },
4781
4782     doInsert : function(where, el, values, returnEl){
4783         el = Roo.getDom(el);
4784         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4785         return returnEl ? Roo.get(newNode, true) : newNode;
4786     },
4787
4788     /**
4789      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4790      * @param {String/HTMLElement/Roo.Element} el The context element
4791      * @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'})
4792      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4793      * @return {HTMLElement/Roo.Element} The new node or Element
4794      */
4795     overwrite : function(el, values, returnElement){
4796         el = Roo.getDom(el);
4797         el.innerHTML = this.applyTemplate(values);
4798         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4799     }
4800 };
4801 /**
4802  * Alias for {@link #applyTemplate}
4803  * @method
4804  */
4805 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4806
4807 // backwards compat
4808 Roo.DomHelper.Template = Roo.Template;
4809
4810 /**
4811  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4812  * @param {String/HTMLElement} el A DOM element or its id
4813  * @returns {Roo.Template} The created template
4814  * @static
4815  */
4816 Roo.Template.from = function(el){
4817     el = Roo.getDom(el);
4818     return new Roo.Template(el.value || el.innerHTML);
4819 };/*
4820  * Based on:
4821  * Ext JS Library 1.1.1
4822  * Copyright(c) 2006-2007, Ext JS, LLC.
4823  *
4824  * Originally Released Under LGPL - original licence link has changed is not relivant.
4825  *
4826  * Fork - LGPL
4827  * <script type="text/javascript">
4828  */
4829  
4830
4831 /*
4832  * This is code is also distributed under MIT license for use
4833  * with jQuery and prototype JavaScript libraries.
4834  */
4835 /**
4836  * @class Roo.DomQuery
4837 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).
4838 <p>
4839 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>
4840
4841 <p>
4842 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.
4843 </p>
4844 <h4>Element Selectors:</h4>
4845 <ul class="list">
4846     <li> <b>*</b> any element</li>
4847     <li> <b>E</b> an element with the tag E</li>
4848     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4849     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4850     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4851     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4852 </ul>
4853 <h4>Attribute Selectors:</h4>
4854 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4855 <ul class="list">
4856     <li> <b>E[foo]</b> has an attribute "foo"</li>
4857     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4858     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4859     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4860     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4861     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4862     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4863 </ul>
4864 <h4>Pseudo Classes:</h4>
4865 <ul class="list">
4866     <li> <b>E:first-child</b> E is the first child of its parent</li>
4867     <li> <b>E:last-child</b> E is the last child of its parent</li>
4868     <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>
4869     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4870     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4871     <li> <b>E:only-child</b> E is the only child of its parent</li>
4872     <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>
4873     <li> <b>E:first</b> the first E in the resultset</li>
4874     <li> <b>E:last</b> the last E in the resultset</li>
4875     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4876     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4877     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4878     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4879     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4880     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4881     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4882     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4883     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4884 </ul>
4885 <h4>CSS Value Selectors:</h4>
4886 <ul class="list">
4887     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4888     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4889     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4890     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4891     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4892     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4893 </ul>
4894  * @singleton
4895  */
4896 Roo.DomQuery = function(){
4897     var cache = {}, simpleCache = {}, valueCache = {};
4898     var nonSpace = /\S/;
4899     var trimRe = /^\s+|\s+$/g;
4900     var tplRe = /\{(\d+)\}/g;
4901     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4902     var tagTokenRe = /^(#)?([\w-\*]+)/;
4903     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4904
4905     function child(p, index){
4906         var i = 0;
4907         var n = p.firstChild;
4908         while(n){
4909             if(n.nodeType == 1){
4910                if(++i == index){
4911                    return n;
4912                }
4913             }
4914             n = n.nextSibling;
4915         }
4916         return null;
4917     };
4918
4919     function next(n){
4920         while((n = n.nextSibling) && n.nodeType != 1);
4921         return n;
4922     };
4923
4924     function prev(n){
4925         while((n = n.previousSibling) && n.nodeType != 1);
4926         return n;
4927     };
4928
4929     function children(d){
4930         var n = d.firstChild, ni = -1;
4931             while(n){
4932                 var nx = n.nextSibling;
4933                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4934                     d.removeChild(n);
4935                 }else{
4936                     n.nodeIndex = ++ni;
4937                 }
4938                 n = nx;
4939             }
4940             return this;
4941         };
4942
4943     function byClassName(c, a, v){
4944         if(!v){
4945             return c;
4946         }
4947         var r = [], ri = -1, cn;
4948         for(var i = 0, ci; ci = c[i]; i++){
4949             if((' '+ci.className+' ').indexOf(v) != -1){
4950                 r[++ri] = ci;
4951             }
4952         }
4953         return r;
4954     };
4955
4956     function attrValue(n, attr){
4957         if(!n.tagName && typeof n.length != "undefined"){
4958             n = n[0];
4959         }
4960         if(!n){
4961             return null;
4962         }
4963         if(attr == "for"){
4964             return n.htmlFor;
4965         }
4966         if(attr == "class" || attr == "className"){
4967             return n.className;
4968         }
4969         return n.getAttribute(attr) || n[attr];
4970
4971     };
4972
4973     function getNodes(ns, mode, tagName){
4974         var result = [], ri = -1, cs;
4975         if(!ns){
4976             return result;
4977         }
4978         tagName = tagName || "*";
4979         if(typeof ns.getElementsByTagName != "undefined"){
4980             ns = [ns];
4981         }
4982         if(!mode){
4983             for(var i = 0, ni; ni = ns[i]; i++){
4984                 cs = ni.getElementsByTagName(tagName);
4985                 for(var j = 0, ci; ci = cs[j]; j++){
4986                     result[++ri] = ci;
4987                 }
4988             }
4989         }else if(mode == "/" || mode == ">"){
4990             var utag = tagName.toUpperCase();
4991             for(var i = 0, ni, cn; ni = ns[i]; i++){
4992                 cn = ni.children || ni.childNodes;
4993                 for(var j = 0, cj; cj = cn[j]; j++){
4994                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4995                         result[++ri] = cj;
4996                     }
4997                 }
4998             }
4999         }else if(mode == "+"){
5000             var utag = tagName.toUpperCase();
5001             for(var i = 0, n; n = ns[i]; i++){
5002                 while((n = n.nextSibling) && n.nodeType != 1);
5003                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5004                     result[++ri] = n;
5005                 }
5006             }
5007         }else if(mode == "~"){
5008             for(var i = 0, n; n = ns[i]; i++){
5009                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5010                 if(n){
5011                     result[++ri] = n;
5012                 }
5013             }
5014         }
5015         return result;
5016     };
5017
5018     function concat(a, b){
5019         if(b.slice){
5020             return a.concat(b);
5021         }
5022         for(var i = 0, l = b.length; i < l; i++){
5023             a[a.length] = b[i];
5024         }
5025         return a;
5026     }
5027
5028     function byTag(cs, tagName){
5029         if(cs.tagName || cs == document){
5030             cs = [cs];
5031         }
5032         if(!tagName){
5033             return cs;
5034         }
5035         var r = [], ri = -1;
5036         tagName = tagName.toLowerCase();
5037         for(var i = 0, ci; ci = cs[i]; i++){
5038             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5039                 r[++ri] = ci;
5040             }
5041         }
5042         return r;
5043     };
5044
5045     function byId(cs, attr, id){
5046         if(cs.tagName || cs == document){
5047             cs = [cs];
5048         }
5049         if(!id){
5050             return cs;
5051         }
5052         var r = [], ri = -1;
5053         for(var i = 0,ci; ci = cs[i]; i++){
5054             if(ci && ci.id == id){
5055                 r[++ri] = ci;
5056                 return r;
5057             }
5058         }
5059         return r;
5060     };
5061
5062     function byAttribute(cs, attr, value, op, custom){
5063         var r = [], ri = -1, st = custom=="{";
5064         var f = Roo.DomQuery.operators[op];
5065         for(var i = 0, ci; ci = cs[i]; i++){
5066             var a;
5067             if(st){
5068                 a = Roo.DomQuery.getStyle(ci, attr);
5069             }
5070             else if(attr == "class" || attr == "className"){
5071                 a = ci.className;
5072             }else if(attr == "for"){
5073                 a = ci.htmlFor;
5074             }else if(attr == "href"){
5075                 a = ci.getAttribute("href", 2);
5076             }else{
5077                 a = ci.getAttribute(attr);
5078             }
5079             if((f && f(a, value)) || (!f && a)){
5080                 r[++ri] = ci;
5081             }
5082         }
5083         return r;
5084     };
5085
5086     function byPseudo(cs, name, value){
5087         return Roo.DomQuery.pseudos[name](cs, value);
5088     };
5089
5090     // This is for IE MSXML which does not support expandos.
5091     // IE runs the same speed using setAttribute, however FF slows way down
5092     // and Safari completely fails so they need to continue to use expandos.
5093     var isIE = window.ActiveXObject ? true : false;
5094
5095     // this eval is stop the compressor from
5096     // renaming the variable to something shorter
5097     
5098     /** eval:var:batch */
5099     var batch = 30803; 
5100
5101     var key = 30803;
5102
5103     function nodupIEXml(cs){
5104         var d = ++key;
5105         cs[0].setAttribute("_nodup", d);
5106         var r = [cs[0]];
5107         for(var i = 1, len = cs.length; i < len; i++){
5108             var c = cs[i];
5109             if(!c.getAttribute("_nodup") != d){
5110                 c.setAttribute("_nodup", d);
5111                 r[r.length] = c;
5112             }
5113         }
5114         for(var i = 0, len = cs.length; i < len; i++){
5115             cs[i].removeAttribute("_nodup");
5116         }
5117         return r;
5118     }
5119
5120     function nodup(cs){
5121         if(!cs){
5122             return [];
5123         }
5124         var len = cs.length, c, i, r = cs, cj, ri = -1;
5125         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5126             return cs;
5127         }
5128         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5129             return nodupIEXml(cs);
5130         }
5131         var d = ++key;
5132         cs[0]._nodup = d;
5133         for(i = 1; c = cs[i]; i++){
5134             if(c._nodup != d){
5135                 c._nodup = d;
5136             }else{
5137                 r = [];
5138                 for(var j = 0; j < i; j++){
5139                     r[++ri] = cs[j];
5140                 }
5141                 for(j = i+1; cj = cs[j]; j++){
5142                     if(cj._nodup != d){
5143                         cj._nodup = d;
5144                         r[++ri] = cj;
5145                     }
5146                 }
5147                 return r;
5148             }
5149         }
5150         return r;
5151     }
5152
5153     function quickDiffIEXml(c1, c2){
5154         var d = ++key;
5155         for(var i = 0, len = c1.length; i < len; i++){
5156             c1[i].setAttribute("_qdiff", d);
5157         }
5158         var r = [];
5159         for(var i = 0, len = c2.length; i < len; i++){
5160             if(c2[i].getAttribute("_qdiff") != d){
5161                 r[r.length] = c2[i];
5162             }
5163         }
5164         for(var i = 0, len = c1.length; i < len; i++){
5165            c1[i].removeAttribute("_qdiff");
5166         }
5167         return r;
5168     }
5169
5170     function quickDiff(c1, c2){
5171         var len1 = c1.length;
5172         if(!len1){
5173             return c2;
5174         }
5175         if(isIE && c1[0].selectSingleNode){
5176             return quickDiffIEXml(c1, c2);
5177         }
5178         var d = ++key;
5179         for(var i = 0; i < len1; i++){
5180             c1[i]._qdiff = d;
5181         }
5182         var r = [];
5183         for(var i = 0, len = c2.length; i < len; i++){
5184             if(c2[i]._qdiff != d){
5185                 r[r.length] = c2[i];
5186             }
5187         }
5188         return r;
5189     }
5190
5191     function quickId(ns, mode, root, id){
5192         if(ns == root){
5193            var d = root.ownerDocument || root;
5194            return d.getElementById(id);
5195         }
5196         ns = getNodes(ns, mode, "*");
5197         return byId(ns, null, id);
5198     }
5199
5200     return {
5201         getStyle : function(el, name){
5202             return Roo.fly(el).getStyle(name);
5203         },
5204         /**
5205          * Compiles a selector/xpath query into a reusable function. The returned function
5206          * takes one parameter "root" (optional), which is the context node from where the query should start.
5207          * @param {String} selector The selector/xpath query
5208          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5209          * @return {Function}
5210          */
5211         compile : function(path, type){
5212             type = type || "select";
5213             
5214             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5215             var q = path, mode, lq;
5216             var tk = Roo.DomQuery.matchers;
5217             var tklen = tk.length;
5218             var mm;
5219
5220             // accept leading mode switch
5221             var lmode = q.match(modeRe);
5222             if(lmode && lmode[1]){
5223                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5224                 q = q.replace(lmode[1], "");
5225             }
5226             // strip leading slashes
5227             while(path.substr(0, 1)=="/"){
5228                 path = path.substr(1);
5229             }
5230
5231             while(q && lq != q){
5232                 lq = q;
5233                 var tm = q.match(tagTokenRe);
5234                 if(type == "select"){
5235                     if(tm){
5236                         if(tm[1] == "#"){
5237                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5238                         }else{
5239                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5240                         }
5241                         q = q.replace(tm[0], "");
5242                     }else if(q.substr(0, 1) != '@'){
5243                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5244                     }
5245                 }else{
5246                     if(tm){
5247                         if(tm[1] == "#"){
5248                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5249                         }else{
5250                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5251                         }
5252                         q = q.replace(tm[0], "");
5253                     }
5254                 }
5255                 while(!(mm = q.match(modeRe))){
5256                     var matched = false;
5257                     for(var j = 0; j < tklen; j++){
5258                         var t = tk[j];
5259                         var m = q.match(t.re);
5260                         if(m){
5261                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5262                                                     return m[i];
5263                                                 });
5264                             q = q.replace(m[0], "");
5265                             matched = true;
5266                             break;
5267                         }
5268                     }
5269                     // prevent infinite loop on bad selector
5270                     if(!matched){
5271                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5272                     }
5273                 }
5274                 if(mm[1]){
5275                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5276                     q = q.replace(mm[1], "");
5277                 }
5278             }
5279             fn[fn.length] = "return nodup(n);\n}";
5280             
5281              /** 
5282               * list of variables that need from compression as they are used by eval.
5283              *  eval:var:batch 
5284              *  eval:var:nodup
5285              *  eval:var:byTag
5286              *  eval:var:ById
5287              *  eval:var:getNodes
5288              *  eval:var:quickId
5289              *  eval:var:mode
5290              *  eval:var:root
5291              *  eval:var:n
5292              *  eval:var:byClassName
5293              *  eval:var:byPseudo
5294              *  eval:var:byAttribute
5295              *  eval:var:attrValue
5296              * 
5297              **/ 
5298             eval(fn.join(""));
5299             return f;
5300         },
5301
5302         /**
5303          * Selects a group of elements.
5304          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5305          * @param {Node} root (optional) The start of the query (defaults to document).
5306          * @return {Array}
5307          */
5308         select : function(path, root, type){
5309             if(!root || root == document){
5310                 root = document;
5311             }
5312             if(typeof root == "string"){
5313                 root = document.getElementById(root);
5314             }
5315             var paths = path.split(",");
5316             var results = [];
5317             for(var i = 0, len = paths.length; i < len; i++){
5318                 var p = paths[i].replace(trimRe, "");
5319                 if(!cache[p]){
5320                     cache[p] = Roo.DomQuery.compile(p);
5321                     if(!cache[p]){
5322                         throw p + " is not a valid selector";
5323                     }
5324                 }
5325                 var result = cache[p](root);
5326                 if(result && result != document){
5327                     results = results.concat(result);
5328                 }
5329             }
5330             if(paths.length > 1){
5331                 return nodup(results);
5332             }
5333             return results;
5334         },
5335
5336         /**
5337          * Selects a single element.
5338          * @param {String} selector The selector/xpath query
5339          * @param {Node} root (optional) The start of the query (defaults to document).
5340          * @return {Element}
5341          */
5342         selectNode : function(path, root){
5343             return Roo.DomQuery.select(path, root)[0];
5344         },
5345
5346         /**
5347          * Selects the value of a node, optionally replacing null with the defaultValue.
5348          * @param {String} selector The selector/xpath query
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @param {String} defaultValue
5351          */
5352         selectValue : function(path, root, defaultValue){
5353             path = path.replace(trimRe, "");
5354             if(!valueCache[path]){
5355                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5356             }
5357             var n = valueCache[path](root);
5358             n = n[0] ? n[0] : n;
5359             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5360             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5361         },
5362
5363         /**
5364          * Selects the value of a node, parsing integers and floats.
5365          * @param {String} selector The selector/xpath query
5366          * @param {Node} root (optional) The start of the query (defaults to document).
5367          * @param {Number} defaultValue
5368          * @return {Number}
5369          */
5370         selectNumber : function(path, root, defaultValue){
5371             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5372             return parseFloat(v);
5373         },
5374
5375         /**
5376          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5377          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5378          * @param {String} selector The simple selector to test
5379          * @return {Boolean}
5380          */
5381         is : function(el, ss){
5382             if(typeof el == "string"){
5383                 el = document.getElementById(el);
5384             }
5385             var isArray = (el instanceof Array);
5386             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5387             return isArray ? (result.length == el.length) : (result.length > 0);
5388         },
5389
5390         /**
5391          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5392          * @param {Array} el An array of elements to filter
5393          * @param {String} selector The simple selector to test
5394          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5395          * the selector instead of the ones that match
5396          * @return {Array}
5397          */
5398         filter : function(els, ss, nonMatches){
5399             ss = ss.replace(trimRe, "");
5400             if(!simpleCache[ss]){
5401                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5402             }
5403             var result = simpleCache[ss](els);
5404             return nonMatches ? quickDiff(result, els) : result;
5405         },
5406
5407         /**
5408          * Collection of matching regular expressions and code snippets.
5409          */
5410         matchers : [{
5411                 re: /^\.([\w-]+)/,
5412                 select: 'n = byClassName(n, null, " {1} ");'
5413             }, {
5414                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5415                 select: 'n = byPseudo(n, "{1}", "{2}");'
5416             },{
5417                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5418                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5419             }, {
5420                 re: /^#([\w-]+)/,
5421                 select: 'n = byId(n, null, "{1}");'
5422             },{
5423                 re: /^@([\w-]+)/,
5424                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5425             }
5426         ],
5427
5428         /**
5429          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5430          * 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;.
5431          */
5432         operators : {
5433             "=" : function(a, v){
5434                 return a == v;
5435             },
5436             "!=" : function(a, v){
5437                 return a != v;
5438             },
5439             "^=" : function(a, v){
5440                 return a && a.substr(0, v.length) == v;
5441             },
5442             "$=" : function(a, v){
5443                 return a && a.substr(a.length-v.length) == v;
5444             },
5445             "*=" : function(a, v){
5446                 return a && a.indexOf(v) !== -1;
5447             },
5448             "%=" : function(a, v){
5449                 return (a % v) == 0;
5450             },
5451             "|=" : function(a, v){
5452                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5453             },
5454             "~=" : function(a, v){
5455                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5456             }
5457         },
5458
5459         /**
5460          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5461          * and the argument (if any) supplied in the selector.
5462          */
5463         pseudos : {
5464             "first-child" : function(c){
5465                 var r = [], ri = -1, n;
5466                 for(var i = 0, ci; ci = n = c[i]; i++){
5467                     while((n = n.previousSibling) && n.nodeType != 1);
5468                     if(!n){
5469                         r[++ri] = ci;
5470                     }
5471                 }
5472                 return r;
5473             },
5474
5475             "last-child" : function(c){
5476                 var r = [], ri = -1, n;
5477                 for(var i = 0, ci; ci = n = c[i]; i++){
5478                     while((n = n.nextSibling) && n.nodeType != 1);
5479                     if(!n){
5480                         r[++ri] = ci;
5481                     }
5482                 }
5483                 return r;
5484             },
5485
5486             "nth-child" : function(c, a) {
5487                 var r = [], ri = -1;
5488                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5489                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5490                 for(var i = 0, n; n = c[i]; i++){
5491                     var pn = n.parentNode;
5492                     if (batch != pn._batch) {
5493                         var j = 0;
5494                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5495                             if(cn.nodeType == 1){
5496                                cn.nodeIndex = ++j;
5497                             }
5498                         }
5499                         pn._batch = batch;
5500                     }
5501                     if (f == 1) {
5502                         if (l == 0 || n.nodeIndex == l){
5503                             r[++ri] = n;
5504                         }
5505                     } else if ((n.nodeIndex + l) % f == 0){
5506                         r[++ri] = n;
5507                     }
5508                 }
5509
5510                 return r;
5511             },
5512
5513             "only-child" : function(c){
5514                 var r = [], ri = -1;;
5515                 for(var i = 0, ci; ci = c[i]; i++){
5516                     if(!prev(ci) && !next(ci)){
5517                         r[++ri] = ci;
5518                     }
5519                 }
5520                 return r;
5521             },
5522
5523             "empty" : function(c){
5524                 var r = [], ri = -1;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     var cns = ci.childNodes, j = 0, cn, empty = true;
5527                     while(cn = cns[j]){
5528                         ++j;
5529                         if(cn.nodeType == 1 || cn.nodeType == 3){
5530                             empty = false;
5531                             break;
5532                         }
5533                     }
5534                     if(empty){
5535                         r[++ri] = ci;
5536                     }
5537                 }
5538                 return r;
5539             },
5540
5541             "contains" : function(c, v){
5542                 var r = [], ri = -1;
5543                 for(var i = 0, ci; ci = c[i]; i++){
5544                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "nodeValue" : function(c, v){
5552                 var r = [], ri = -1;
5553                 for(var i = 0, ci; ci = c[i]; i++){
5554                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5555                         r[++ri] = ci;
5556                     }
5557                 }
5558                 return r;
5559             },
5560
5561             "checked" : function(c){
5562                 var r = [], ri = -1;
5563                 for(var i = 0, ci; ci = c[i]; i++){
5564                     if(ci.checked == true){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "not" : function(c, ss){
5572                 return Roo.DomQuery.filter(c, ss, true);
5573             },
5574
5575             "odd" : function(c){
5576                 return this["nth-child"](c, "odd");
5577             },
5578
5579             "even" : function(c){
5580                 return this["nth-child"](c, "even");
5581             },
5582
5583             "nth" : function(c, a){
5584                 return c[a-1] || [];
5585             },
5586
5587             "first" : function(c){
5588                 return c[0] || [];
5589             },
5590
5591             "last" : function(c){
5592                 return c[c.length-1] || [];
5593             },
5594
5595             "has" : function(c, ss){
5596                 var s = Roo.DomQuery.select;
5597                 var r = [], ri = -1;
5598                 for(var i = 0, ci; ci = c[i]; i++){
5599                     if(s(ss, ci).length > 0){
5600                         r[++ri] = ci;
5601                     }
5602                 }
5603                 return r;
5604             },
5605
5606             "next" : function(c, ss){
5607                 var is = Roo.DomQuery.is;
5608                 var r = [], ri = -1;
5609                 for(var i = 0, ci; ci = c[i]; i++){
5610                     var n = next(ci);
5611                     if(n && is(n, ss)){
5612                         r[++ri] = ci;
5613                     }
5614                 }
5615                 return r;
5616             },
5617
5618             "prev" : function(c, ss){
5619                 var is = Roo.DomQuery.is;
5620                 var r = [], ri = -1;
5621                 for(var i = 0, ci; ci = c[i]; i++){
5622                     var n = prev(ci);
5623                     if(n && is(n, ss)){
5624                         r[++ri] = ci;
5625                     }
5626                 }
5627                 return r;
5628             }
5629         }
5630     };
5631 }();
5632
5633 /**
5634  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5635  * @param {String} path The selector/xpath query
5636  * @param {Node} root (optional) The start of the query (defaults to document).
5637  * @return {Array}
5638  * @member Roo
5639  * @method query
5640  */
5641 Roo.query = Roo.DomQuery.select;
5642 /*
5643  * Based on:
5644  * Ext JS Library 1.1.1
5645  * Copyright(c) 2006-2007, Ext JS, LLC.
5646  *
5647  * Originally Released Under LGPL - original licence link has changed is not relivant.
5648  *
5649  * Fork - LGPL
5650  * <script type="text/javascript">
5651  */
5652
5653 /**
5654  * @class Roo.util.Observable
5655  * Base class that provides a common interface for publishing events. Subclasses are expected to
5656  * to have a property "events" with all the events defined.<br>
5657  * For example:
5658  * <pre><code>
5659  Employee = function(name){
5660     this.name = name;
5661     this.addEvents({
5662         "fired" : true,
5663         "quit" : true
5664     });
5665  }
5666  Roo.extend(Employee, Roo.util.Observable);
5667 </code></pre>
5668  * @param {Object} config properties to use (incuding events / listeners)
5669  */
5670
5671 Roo.util.Observable = function(cfg){
5672     
5673     cfg = cfg|| {};
5674     this.addEvents(cfg.events || {});
5675     if (cfg.events) {
5676         delete cfg.events; // make sure
5677     }
5678      
5679     Roo.apply(this, cfg);
5680     
5681     if(this.listeners){
5682         this.on(this.listeners);
5683         delete this.listeners;
5684     }
5685 };
5686 Roo.util.Observable.prototype = {
5687     /** 
5688  * @cfg {Object} listeners  list of events and functions to call for this object, 
5689  * For example :
5690  * <pre><code>
5691     listeners :  { 
5692        'click' : function(e) {
5693            ..... 
5694         } ,
5695         .... 
5696     } 
5697   </code></pre>
5698  */
5699     
5700     
5701     /**
5702      * Fires the specified event with the passed parameters (minus the event name).
5703      * @param {String} eventName
5704      * @param {Object...} args Variable number of parameters are passed to handlers
5705      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5706      */
5707     fireEvent : function(){
5708         var ce = this.events[arguments[0].toLowerCase()];
5709         if(typeof ce == "object"){
5710             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5711         }else{
5712             return true;
5713         }
5714     },
5715
5716     // private
5717     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5718
5719     /**
5720      * Appends an event handler to this component
5721      * @param {String}   eventName The type of event to listen for
5722      * @param {Function} handler The method the event invokes
5723      * @param {Object}   scope (optional) The scope in which to execute the handler
5724      * function. The handler function's "this" context.
5725      * @param {Object}   options (optional) An object containing handler configuration
5726      * properties. This may contain any of the following properties:<ul>
5727      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5728      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5729      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5730      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5731      * by the specified number of milliseconds. If the event fires again within that time, the original
5732      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5733      * </ul><br>
5734      * <p>
5735      * <b>Combining Options</b><br>
5736      * Using the options argument, it is possible to combine different types of listeners:<br>
5737      * <br>
5738      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5739                 <pre><code>
5740                 el.on('click', this.onClick, this, {
5741                         single: true,
5742                 delay: 100,
5743                 forumId: 4
5744                 });
5745                 </code></pre>
5746      * <p>
5747      * <b>Attaching multiple handlers in 1 call</b><br>
5748      * The method also allows for a single argument to be passed which is a config object containing properties
5749      * which specify multiple handlers.
5750      * <pre><code>
5751                 el.on({
5752                         'click': {
5753                         fn: this.onClick,
5754                         scope: this,
5755                         delay: 100
5756                 }, 
5757                 'mouseover': {
5758                         fn: this.onMouseOver,
5759                         scope: this
5760                 },
5761                 'mouseout': {
5762                         fn: this.onMouseOut,
5763                         scope: this
5764                 }
5765                 });
5766                 </code></pre>
5767      * <p>
5768      * Or a shorthand syntax which passes the same scope object to all handlers:
5769         <pre><code>
5770                 el.on({
5771                         'click': this.onClick,
5772                 'mouseover': this.onMouseOver,
5773                 'mouseout': this.onMouseOut,
5774                 scope: this
5775                 });
5776                 </code></pre>
5777      */
5778     addListener : function(eventName, fn, scope, o){
5779         if(typeof eventName == "object"){
5780             o = eventName;
5781             for(var e in o){
5782                 if(this.filterOptRe.test(e)){
5783                     continue;
5784                 }
5785                 if(typeof o[e] == "function"){
5786                     // shared options
5787                     this.addListener(e, o[e], o.scope,  o);
5788                 }else{
5789                     // individual options
5790                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5791                 }
5792             }
5793             return;
5794         }
5795         o = (!o || typeof o == "boolean") ? {} : o;
5796         eventName = eventName.toLowerCase();
5797         var ce = this.events[eventName] || true;
5798         if(typeof ce == "boolean"){
5799             ce = new Roo.util.Event(this, eventName);
5800             this.events[eventName] = ce;
5801         }
5802         ce.addListener(fn, scope, o);
5803     },
5804
5805     /**
5806      * Removes a listener
5807      * @param {String}   eventName     The type of event to listen for
5808      * @param {Function} handler        The handler to remove
5809      * @param {Object}   scope  (optional) The scope (this object) for the handler
5810      */
5811     removeListener : function(eventName, fn, scope){
5812         var ce = this.events[eventName.toLowerCase()];
5813         if(typeof ce == "object"){
5814             ce.removeListener(fn, scope);
5815         }
5816     },
5817
5818     /**
5819      * Removes all listeners for this object
5820      */
5821     purgeListeners : function(){
5822         for(var evt in this.events){
5823             if(typeof this.events[evt] == "object"){
5824                  this.events[evt].clearListeners();
5825             }
5826         }
5827     },
5828
5829     relayEvents : function(o, events){
5830         var createHandler = function(ename){
5831             return function(){
5832                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5833             };
5834         };
5835         for(var i = 0, len = events.length; i < len; i++){
5836             var ename = events[i];
5837             if(!this.events[ename]){ this.events[ename] = true; };
5838             o.on(ename, createHandler(ename), this);
5839         }
5840     },
5841
5842     /**
5843      * Used to define events on this Observable
5844      * @param {Object} object The object with the events defined
5845      */
5846     addEvents : function(o){
5847         if(!this.events){
5848             this.events = {};
5849         }
5850         Roo.applyIf(this.events, o);
5851     },
5852
5853     /**
5854      * Checks to see if this object has any listeners for a specified event
5855      * @param {String} eventName The name of the event to check for
5856      * @return {Boolean} True if the event is being listened for, else false
5857      */
5858     hasListener : function(eventName){
5859         var e = this.events[eventName];
5860         return typeof e == "object" && e.listeners.length > 0;
5861     }
5862 };
5863 /**
5864  * Appends an event handler to this element (shorthand for addListener)
5865  * @param {String}   eventName     The type of event to listen for
5866  * @param {Function} handler        The method the event invokes
5867  * @param {Object}   scope (optional) The scope in which to execute the handler
5868  * function. The handler function's "this" context.
5869  * @param {Object}   options  (optional)
5870  * @method
5871  */
5872 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5873 /**
5874  * Removes a listener (shorthand for removeListener)
5875  * @param {String}   eventName     The type of event to listen for
5876  * @param {Function} handler        The handler to remove
5877  * @param {Object}   scope  (optional) The scope (this object) for the handler
5878  * @method
5879  */
5880 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5881
5882 /**
5883  * Starts capture on the specified Observable. All events will be passed
5884  * to the supplied function with the event name + standard signature of the event
5885  * <b>before</b> the event is fired. If the supplied function returns false,
5886  * the event will not fire.
5887  * @param {Observable} o The Observable to capture
5888  * @param {Function} fn The function to call
5889  * @param {Object} scope (optional) The scope (this object) for the fn
5890  * @static
5891  */
5892 Roo.util.Observable.capture = function(o, fn, scope){
5893     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5894 };
5895
5896 /**
5897  * Removes <b>all</b> added captures from the Observable.
5898  * @param {Observable} o The Observable to release
5899  * @static
5900  */
5901 Roo.util.Observable.releaseCapture = function(o){
5902     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5903 };
5904
5905 (function(){
5906
5907     var createBuffered = function(h, o, scope){
5908         var task = new Roo.util.DelayedTask();
5909         return function(){
5910             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5911         };
5912     };
5913
5914     var createSingle = function(h, e, fn, scope){
5915         return function(){
5916             e.removeListener(fn, scope);
5917             return h.apply(scope, arguments);
5918         };
5919     };
5920
5921     var createDelayed = function(h, o, scope){
5922         return function(){
5923             var args = Array.prototype.slice.call(arguments, 0);
5924             setTimeout(function(){
5925                 h.apply(scope, args);
5926             }, o.delay || 10);
5927         };
5928     };
5929
5930     Roo.util.Event = function(obj, name){
5931         this.name = name;
5932         this.obj = obj;
5933         this.listeners = [];
5934     };
5935
5936     Roo.util.Event.prototype = {
5937         addListener : function(fn, scope, options){
5938             var o = options || {};
5939             scope = scope || this.obj;
5940             if(!this.isListening(fn, scope)){
5941                 var l = {fn: fn, scope: scope, options: o};
5942                 var h = fn;
5943                 if(o.delay){
5944                     h = createDelayed(h, o, scope);
5945                 }
5946                 if(o.single){
5947                     h = createSingle(h, this, fn, scope);
5948                 }
5949                 if(o.buffer){
5950                     h = createBuffered(h, o, scope);
5951                 }
5952                 l.fireFn = h;
5953                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5954                     this.listeners.push(l);
5955                 }else{
5956                     this.listeners = this.listeners.slice(0);
5957                     this.listeners.push(l);
5958                 }
5959             }
5960         },
5961
5962         findListener : function(fn, scope){
5963             scope = scope || this.obj;
5964             var ls = this.listeners;
5965             for(var i = 0, len = ls.length; i < len; i++){
5966                 var l = ls[i];
5967                 if(l.fn == fn && l.scope == scope){
5968                     return i;
5969                 }
5970             }
5971             return -1;
5972         },
5973
5974         isListening : function(fn, scope){
5975             return this.findListener(fn, scope) != -1;
5976         },
5977
5978         removeListener : function(fn, scope){
5979             var index;
5980             if((index = this.findListener(fn, scope)) != -1){
5981                 if(!this.firing){
5982                     this.listeners.splice(index, 1);
5983                 }else{
5984                     this.listeners = this.listeners.slice(0);
5985                     this.listeners.splice(index, 1);
5986                 }
5987                 return true;
5988             }
5989             return false;
5990         },
5991
5992         clearListeners : function(){
5993             this.listeners = [];
5994         },
5995
5996         fire : function(){
5997             var ls = this.listeners, scope, len = ls.length;
5998             if(len > 0){
5999                 this.firing = true;
6000                 var args = Array.prototype.slice.call(arguments, 0);
6001                 for(var i = 0; i < len; i++){
6002                     var l = ls[i];
6003                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6004                         this.firing = false;
6005                         return false;
6006                     }
6007                 }
6008                 this.firing = false;
6009             }
6010             return true;
6011         }
6012     };
6013 })();/*
6014  * Based on:
6015  * Ext JS Library 1.1.1
6016  * Copyright(c) 2006-2007, Ext JS, LLC.
6017  *
6018  * Originally Released Under LGPL - original licence link has changed is not relivant.
6019  *
6020  * Fork - LGPL
6021  * <script type="text/javascript">
6022  */
6023
6024 /**
6025  * @class Roo.EventManager
6026  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6027  * several useful events directly.
6028  * See {@link Roo.EventObject} for more details on normalized event objects.
6029  * @singleton
6030  */
6031 Roo.EventManager = function(){
6032     var docReadyEvent, docReadyProcId, docReadyState = false;
6033     var resizeEvent, resizeTask, textEvent, textSize;
6034     var E = Roo.lib.Event;
6035     var D = Roo.lib.Dom;
6036
6037
6038     var fireDocReady = function(){
6039         if(!docReadyState){
6040             docReadyState = true;
6041             Roo.isReady = true;
6042             if(docReadyProcId){
6043                 clearInterval(docReadyProcId);
6044             }
6045             if(Roo.isGecko || Roo.isOpera) {
6046                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6047             }
6048             if(Roo.isIE){
6049                 var defer = document.getElementById("ie-deferred-loader");
6050                 if(defer){
6051                     defer.onreadystatechange = null;
6052                     defer.parentNode.removeChild(defer);
6053                 }
6054             }
6055             if(docReadyEvent){
6056                 docReadyEvent.fire();
6057                 docReadyEvent.clearListeners();
6058             }
6059         }
6060     };
6061     
6062     var initDocReady = function(){
6063         docReadyEvent = new Roo.util.Event();
6064         if(Roo.isGecko || Roo.isOpera) {
6065             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6066         }else if(Roo.isIE){
6067             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6068             var defer = document.getElementById("ie-deferred-loader");
6069             defer.onreadystatechange = function(){
6070                 if(this.readyState == "complete"){
6071                     fireDocReady();
6072                 }
6073             };
6074         }else if(Roo.isSafari){ 
6075             docReadyProcId = setInterval(function(){
6076                 var rs = document.readyState;
6077                 if(rs == "complete") {
6078                     fireDocReady();     
6079                  }
6080             }, 10);
6081         }
6082         // no matter what, make sure it fires on load
6083         E.on(window, "load", fireDocReady);
6084     };
6085
6086     var createBuffered = function(h, o){
6087         var task = new Roo.util.DelayedTask(h);
6088         return function(e){
6089             // create new event object impl so new events don't wipe out properties
6090             e = new Roo.EventObjectImpl(e);
6091             task.delay(o.buffer, h, null, [e]);
6092         };
6093     };
6094
6095     var createSingle = function(h, el, ename, fn){
6096         return function(e){
6097             Roo.EventManager.removeListener(el, ename, fn);
6098             h(e);
6099         };
6100     };
6101
6102     var createDelayed = function(h, o){
6103         return function(e){
6104             // create new event object impl so new events don't wipe out properties
6105             e = new Roo.EventObjectImpl(e);
6106             setTimeout(function(){
6107                 h(e);
6108             }, o.delay || 10);
6109         };
6110     };
6111
6112     var listen = function(element, ename, opt, fn, scope){
6113         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6114         fn = fn || o.fn; scope = scope || o.scope;
6115         var el = Roo.getDom(element);
6116         if(!el){
6117             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6118         }
6119         var h = function(e){
6120             e = Roo.EventObject.setEvent(e);
6121             var t;
6122             if(o.delegate){
6123                 t = e.getTarget(o.delegate, el);
6124                 if(!t){
6125                     return;
6126                 }
6127             }else{
6128                 t = e.target;
6129             }
6130             if(o.stopEvent === true){
6131                 e.stopEvent();
6132             }
6133             if(o.preventDefault === true){
6134                e.preventDefault();
6135             }
6136             if(o.stopPropagation === true){
6137                 e.stopPropagation();
6138             }
6139
6140             if(o.normalized === false){
6141                 e = e.browserEvent;
6142             }
6143
6144             fn.call(scope || el, e, t, o);
6145         };
6146         if(o.delay){
6147             h = createDelayed(h, o);
6148         }
6149         if(o.single){
6150             h = createSingle(h, el, ename, fn);
6151         }
6152         if(o.buffer){
6153             h = createBuffered(h, o);
6154         }
6155         fn._handlers = fn._handlers || [];
6156         fn._handlers.push([Roo.id(el), ename, h]);
6157
6158         E.on(el, ename, h);
6159         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6160             el.addEventListener("DOMMouseScroll", h, false);
6161             E.on(window, 'unload', function(){
6162                 el.removeEventListener("DOMMouseScroll", h, false);
6163             });
6164         }
6165         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6166             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6167         }
6168         return h;
6169     };
6170
6171     var stopListening = function(el, ename, fn){
6172         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6173         if(hds){
6174             for(var i = 0, len = hds.length; i < len; i++){
6175                 var h = hds[i];
6176                 if(h[0] == id && h[1] == ename){
6177                     hd = h[2];
6178                     hds.splice(i, 1);
6179                     break;
6180                 }
6181             }
6182         }
6183         E.un(el, ename, hd);
6184         el = Roo.getDom(el);
6185         if(ename == "mousewheel" && el.addEventListener){
6186             el.removeEventListener("DOMMouseScroll", hd, false);
6187         }
6188         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6189             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6190         }
6191     };
6192
6193     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6194     
6195     var pub = {
6196         
6197         
6198         /** 
6199          * Fix for doc tools
6200          * @scope Roo.EventManager
6201          */
6202         
6203         
6204         /** 
6205          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6206          * object with a Roo.EventObject
6207          * @param {Function} fn        The method the event invokes
6208          * @param {Object}   scope    An object that becomes the scope of the handler
6209          * @param {boolean}  override If true, the obj passed in becomes
6210          *                             the execution scope of the listener
6211          * @return {Function} The wrapped function
6212          * @deprecated
6213          */
6214         wrap : function(fn, scope, override){
6215             return function(e){
6216                 Roo.EventObject.setEvent(e);
6217                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6218             };
6219         },
6220         
6221         /**
6222      * Appends an event handler to an element (shorthand for addListener)
6223      * @param {String/HTMLElement}   element        The html element or id to assign the
6224      * @param {String}   eventName The type of event to listen for
6225      * @param {Function} handler The method the event invokes
6226      * @param {Object}   scope (optional) The scope in which to execute the handler
6227      * function. The handler function's "this" context.
6228      * @param {Object}   options (optional) An object containing handler configuration
6229      * properties. This may contain any of the following properties:<ul>
6230      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6231      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6232      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6233      * <li>preventDefault {Boolean} True to prevent the default action</li>
6234      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6235      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6236      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6237      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6238      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6239      * by the specified number of milliseconds. If the event fires again within that time, the original
6240      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6241      * </ul><br>
6242      * <p>
6243      * <b>Combining Options</b><br>
6244      * Using the options argument, it is possible to combine different types of listeners:<br>
6245      * <br>
6246      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6247      * Code:<pre><code>
6248 el.on('click', this.onClick, this, {
6249     single: true,
6250     delay: 100,
6251     stopEvent : true,
6252     forumId: 4
6253 });</code></pre>
6254      * <p>
6255      * <b>Attaching multiple handlers in 1 call</b><br>
6256       * The method also allows for a single argument to be passed which is a config object containing properties
6257      * which specify multiple handlers.
6258      * <p>
6259      * Code:<pre><code>
6260 el.on({
6261     'click' : {
6262         fn: this.onClick
6263         scope: this,
6264         delay: 100
6265     },
6266     'mouseover' : {
6267         fn: this.onMouseOver
6268         scope: this
6269     },
6270     'mouseout' : {
6271         fn: this.onMouseOut
6272         scope: this
6273     }
6274 });</code></pre>
6275      * <p>
6276      * Or a shorthand syntax:<br>
6277      * Code:<pre><code>
6278 el.on({
6279     'click' : this.onClick,
6280     'mouseover' : this.onMouseOver,
6281     'mouseout' : this.onMouseOut
6282     scope: this
6283 });</code></pre>
6284      */
6285         addListener : function(element, eventName, fn, scope, options){
6286             if(typeof eventName == "object"){
6287                 var o = eventName;
6288                 for(var e in o){
6289                     if(propRe.test(e)){
6290                         continue;
6291                     }
6292                     if(typeof o[e] == "function"){
6293                         // shared options
6294                         listen(element, e, o, o[e], o.scope);
6295                     }else{
6296                         // individual options
6297                         listen(element, e, o[e]);
6298                     }
6299                 }
6300                 return;
6301             }
6302             return listen(element, eventName, options, fn, scope);
6303         },
6304         
6305         /**
6306          * Removes an event handler
6307          *
6308          * @param {String/HTMLElement}   element        The id or html element to remove the 
6309          *                             event from
6310          * @param {String}   eventName     The type of event
6311          * @param {Function} fn
6312          * @return {Boolean} True if a listener was actually removed
6313          */
6314         removeListener : function(element, eventName, fn){
6315             return stopListening(element, eventName, fn);
6316         },
6317         
6318         /**
6319          * Fires when the document is ready (before onload and before images are loaded). Can be 
6320          * accessed shorthanded Roo.onReady().
6321          * @param {Function} fn        The method the event invokes
6322          * @param {Object}   scope    An  object that becomes the scope of the handler
6323          * @param {boolean}  options
6324          */
6325         onDocumentReady : function(fn, scope, options){
6326             if(docReadyState){ // if it already fired
6327                 docReadyEvent.addListener(fn, scope, options);
6328                 docReadyEvent.fire();
6329                 docReadyEvent.clearListeners();
6330                 return;
6331             }
6332             if(!docReadyEvent){
6333                 initDocReady();
6334             }
6335             docReadyEvent.addListener(fn, scope, options);
6336         },
6337         
6338         /**
6339          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6340          * @param {Function} fn        The method the event invokes
6341          * @param {Object}   scope    An object that becomes the scope of the handler
6342          * @param {boolean}  options
6343          */
6344         onWindowResize : function(fn, scope, options){
6345             if(!resizeEvent){
6346                 resizeEvent = new Roo.util.Event();
6347                 resizeTask = new Roo.util.DelayedTask(function(){
6348                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6349                 });
6350                 E.on(window, "resize", function(){
6351                     if(Roo.isIE){
6352                         resizeTask.delay(50);
6353                     }else{
6354                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6355                     }
6356                 });
6357             }
6358             resizeEvent.addListener(fn, scope, options);
6359         },
6360
6361         /**
6362          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6363          * @param {Function} fn        The method the event invokes
6364          * @param {Object}   scope    An object that becomes the scope of the handler
6365          * @param {boolean}  options
6366          */
6367         onTextResize : function(fn, scope, options){
6368             if(!textEvent){
6369                 textEvent = new Roo.util.Event();
6370                 var textEl = new Roo.Element(document.createElement('div'));
6371                 textEl.dom.className = 'x-text-resize';
6372                 textEl.dom.innerHTML = 'X';
6373                 textEl.appendTo(document.body);
6374                 textSize = textEl.dom.offsetHeight;
6375                 setInterval(function(){
6376                     if(textEl.dom.offsetHeight != textSize){
6377                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6378                     }
6379                 }, this.textResizeInterval);
6380             }
6381             textEvent.addListener(fn, scope, options);
6382         },
6383
6384         /**
6385          * Removes the passed window resize listener.
6386          * @param {Function} fn        The method the event invokes
6387          * @param {Object}   scope    The scope of handler
6388          */
6389         removeResizeListener : function(fn, scope){
6390             if(resizeEvent){
6391                 resizeEvent.removeListener(fn, scope);
6392             }
6393         },
6394
6395         // private
6396         fireResize : function(){
6397             if(resizeEvent){
6398                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6399             }   
6400         },
6401         /**
6402          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6403          */
6404         ieDeferSrc : false,
6405         /**
6406          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6407          */
6408         textResizeInterval : 50
6409     };
6410     
6411     /**
6412      * Fix for doc tools
6413      * @scopeAlias pub=Roo.EventManager
6414      */
6415     
6416      /**
6417      * Appends an event handler to an element (shorthand for addListener)
6418      * @param {String/HTMLElement}   element        The html element or id to assign the
6419      * @param {String}   eventName The type of event to listen for
6420      * @param {Function} handler The method the event invokes
6421      * @param {Object}   scope (optional) The scope in which to execute the handler
6422      * function. The handler function's "this" context.
6423      * @param {Object}   options (optional) An object containing handler configuration
6424      * properties. This may contain any of the following properties:<ul>
6425      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6426      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6427      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6428      * <li>preventDefault {Boolean} True to prevent the default action</li>
6429      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6430      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6431      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6432      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6433      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6434      * by the specified number of milliseconds. If the event fires again within that time, the original
6435      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6436      * </ul><br>
6437      * <p>
6438      * <b>Combining Options</b><br>
6439      * Using the options argument, it is possible to combine different types of listeners:<br>
6440      * <br>
6441      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6442      * Code:<pre><code>
6443 el.on('click', this.onClick, this, {
6444     single: true,
6445     delay: 100,
6446     stopEvent : true,
6447     forumId: 4
6448 });</code></pre>
6449      * <p>
6450      * <b>Attaching multiple handlers in 1 call</b><br>
6451       * The method also allows for a single argument to be passed which is a config object containing properties
6452      * which specify multiple handlers.
6453      * <p>
6454      * Code:<pre><code>
6455 el.on({
6456     'click' : {
6457         fn: this.onClick
6458         scope: this,
6459         delay: 100
6460     },
6461     'mouseover' : {
6462         fn: this.onMouseOver
6463         scope: this
6464     },
6465     'mouseout' : {
6466         fn: this.onMouseOut
6467         scope: this
6468     }
6469 });</code></pre>
6470      * <p>
6471      * Or a shorthand syntax:<br>
6472      * Code:<pre><code>
6473 el.on({
6474     'click' : this.onClick,
6475     'mouseover' : this.onMouseOver,
6476     'mouseout' : this.onMouseOut
6477     scope: this
6478 });</code></pre>
6479      */
6480     pub.on = pub.addListener;
6481     pub.un = pub.removeListener;
6482
6483     pub.stoppedMouseDownEvent = new Roo.util.Event();
6484     return pub;
6485 }();
6486 /**
6487   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6488   * @param {Function} fn        The method the event invokes
6489   * @param {Object}   scope    An  object that becomes the scope of the handler
6490   * @param {boolean}  override If true, the obj passed in becomes
6491   *                             the execution scope of the listener
6492   * @member Roo
6493   * @method onReady
6494  */
6495 Roo.onReady = Roo.EventManager.onDocumentReady;
6496
6497 Roo.onReady(function(){
6498     var bd = Roo.get(document.body);
6499     if(!bd){ return; }
6500
6501     var cls = [
6502             Roo.isIE ? "roo-ie"
6503             : Roo.isGecko ? "roo-gecko"
6504             : Roo.isOpera ? "roo-opera"
6505             : Roo.isSafari ? "roo-safari" : ""];
6506
6507     if(Roo.isMac){
6508         cls.push("roo-mac");
6509     }
6510     if(Roo.isLinux){
6511         cls.push("roo-linux");
6512     }
6513     if(Roo.isBorderBox){
6514         cls.push('roo-border-box');
6515     }
6516     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6517         var p = bd.dom.parentNode;
6518         if(p){
6519             p.className += ' roo-strict';
6520         }
6521     }
6522     bd.addClass(cls.join(' '));
6523 });
6524
6525 /**
6526  * @class Roo.EventObject
6527  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6528  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6529  * Example:
6530  * <pre><code>
6531  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6532     e.preventDefault();
6533     var target = e.getTarget();
6534     ...
6535  }
6536  var myDiv = Roo.get("myDiv");
6537  myDiv.on("click", handleClick);
6538  //or
6539  Roo.EventManager.on("myDiv", 'click', handleClick);
6540  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6541  </code></pre>
6542  * @singleton
6543  */
6544 Roo.EventObject = function(){
6545     
6546     var E = Roo.lib.Event;
6547     
6548     // safari keypress events for special keys return bad keycodes
6549     var safariKeys = {
6550         63234 : 37, // left
6551         63235 : 39, // right
6552         63232 : 38, // up
6553         63233 : 40, // down
6554         63276 : 33, // page up
6555         63277 : 34, // page down
6556         63272 : 46, // delete
6557         63273 : 36, // home
6558         63275 : 35  // end
6559     };
6560
6561     // normalize button clicks
6562     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6563                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6564
6565     Roo.EventObjectImpl = function(e){
6566         if(e){
6567             this.setEvent(e.browserEvent || e);
6568         }
6569     };
6570     Roo.EventObjectImpl.prototype = {
6571         /**
6572          * Used to fix doc tools.
6573          * @scope Roo.EventObject.prototype
6574          */
6575             
6576
6577         
6578         
6579         /** The normal browser event */
6580         browserEvent : null,
6581         /** The button pressed in a mouse event */
6582         button : -1,
6583         /** True if the shift key was down during the event */
6584         shiftKey : false,
6585         /** True if the control key was down during the event */
6586         ctrlKey : false,
6587         /** True if the alt key was down during the event */
6588         altKey : false,
6589
6590         /** Key constant 
6591         * @type Number */
6592         BACKSPACE : 8,
6593         /** Key constant 
6594         * @type Number */
6595         TAB : 9,
6596         /** Key constant 
6597         * @type Number */
6598         RETURN : 13,
6599         /** Key constant 
6600         * @type Number */
6601         ENTER : 13,
6602         /** Key constant 
6603         * @type Number */
6604         SHIFT : 16,
6605         /** Key constant 
6606         * @type Number */
6607         CONTROL : 17,
6608         /** Key constant 
6609         * @type Number */
6610         ESC : 27,
6611         /** Key constant 
6612         * @type Number */
6613         SPACE : 32,
6614         /** Key constant 
6615         * @type Number */
6616         PAGEUP : 33,
6617         /** Key constant 
6618         * @type Number */
6619         PAGEDOWN : 34,
6620         /** Key constant 
6621         * @type Number */
6622         END : 35,
6623         /** Key constant 
6624         * @type Number */
6625         HOME : 36,
6626         /** Key constant 
6627         * @type Number */
6628         LEFT : 37,
6629         /** Key constant 
6630         * @type Number */
6631         UP : 38,
6632         /** Key constant 
6633         * @type Number */
6634         RIGHT : 39,
6635         /** Key constant 
6636         * @type Number */
6637         DOWN : 40,
6638         /** Key constant 
6639         * @type Number */
6640         DELETE : 46,
6641         /** Key constant 
6642         * @type Number */
6643         F5 : 116,
6644
6645            /** @private */
6646         setEvent : function(e){
6647             if(e == this || (e && e.browserEvent)){ // already wrapped
6648                 return e;
6649             }
6650             this.browserEvent = e;
6651             if(e){
6652                 // normalize buttons
6653                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6654                 if(e.type == 'click' && this.button == -1){
6655                     this.button = 0;
6656                 }
6657                 this.type = e.type;
6658                 this.shiftKey = e.shiftKey;
6659                 // mac metaKey behaves like ctrlKey
6660                 this.ctrlKey = e.ctrlKey || e.metaKey;
6661                 this.altKey = e.altKey;
6662                 // in getKey these will be normalized for the mac
6663                 this.keyCode = e.keyCode;
6664                 // keyup warnings on firefox.
6665                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6666                 // cache the target for the delayed and or buffered events
6667                 this.target = E.getTarget(e);
6668                 // same for XY
6669                 this.xy = E.getXY(e);
6670             }else{
6671                 this.button = -1;
6672                 this.shiftKey = false;
6673                 this.ctrlKey = false;
6674                 this.altKey = false;
6675                 this.keyCode = 0;
6676                 this.charCode =0;
6677                 this.target = null;
6678                 this.xy = [0, 0];
6679             }
6680             return this;
6681         },
6682
6683         /**
6684          * Stop the event (preventDefault and stopPropagation)
6685          */
6686         stopEvent : function(){
6687             if(this.browserEvent){
6688                 if(this.browserEvent.type == 'mousedown'){
6689                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6690                 }
6691                 E.stopEvent(this.browserEvent);
6692             }
6693         },
6694
6695         /**
6696          * Prevents the browsers default handling of the event.
6697          */
6698         preventDefault : function(){
6699             if(this.browserEvent){
6700                 E.preventDefault(this.browserEvent);
6701             }
6702         },
6703
6704         /** @private */
6705         isNavKeyPress : function(){
6706             var k = this.keyCode;
6707             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6708             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6709         },
6710
6711         isSpecialKey : function(){
6712             var k = this.keyCode;
6713             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6714             (k == 16) || (k == 17) ||
6715             (k >= 18 && k <= 20) ||
6716             (k >= 33 && k <= 35) ||
6717             (k >= 36 && k <= 39) ||
6718             (k >= 44 && k <= 45);
6719         },
6720         /**
6721          * Cancels bubbling of the event.
6722          */
6723         stopPropagation : function(){
6724             if(this.browserEvent){
6725                 if(this.type == 'mousedown'){
6726                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6727                 }
6728                 E.stopPropagation(this.browserEvent);
6729             }
6730         },
6731
6732         /**
6733          * Gets the key code for the event.
6734          * @return {Number}
6735          */
6736         getCharCode : function(){
6737             return this.charCode || this.keyCode;
6738         },
6739
6740         /**
6741          * Returns a normalized keyCode for the event.
6742          * @return {Number} The key code
6743          */
6744         getKey : function(){
6745             var k = this.keyCode || this.charCode;
6746             return Roo.isSafari ? (safariKeys[k] || k) : k;
6747         },
6748
6749         /**
6750          * Gets the x coordinate of the event.
6751          * @return {Number}
6752          */
6753         getPageX : function(){
6754             return this.xy[0];
6755         },
6756
6757         /**
6758          * Gets the y coordinate of the event.
6759          * @return {Number}
6760          */
6761         getPageY : function(){
6762             return this.xy[1];
6763         },
6764
6765         /**
6766          * Gets the time of the event.
6767          * @return {Number}
6768          */
6769         getTime : function(){
6770             if(this.browserEvent){
6771                 return E.getTime(this.browserEvent);
6772             }
6773             return null;
6774         },
6775
6776         /**
6777          * Gets the page coordinates of the event.
6778          * @return {Array} The xy values like [x, y]
6779          */
6780         getXY : function(){
6781             return this.xy;
6782         },
6783
6784         /**
6785          * Gets the target for the event.
6786          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6787          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6788                 search as a number or element (defaults to 10 || document.body)
6789          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6790          * @return {HTMLelement}
6791          */
6792         getTarget : function(selector, maxDepth, returnEl){
6793             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6794         },
6795         /**
6796          * Gets the related target.
6797          * @return {HTMLElement}
6798          */
6799         getRelatedTarget : function(){
6800             if(this.browserEvent){
6801                 return E.getRelatedTarget(this.browserEvent);
6802             }
6803             return null;
6804         },
6805
6806         /**
6807          * Normalizes mouse wheel delta across browsers
6808          * @return {Number} The delta
6809          */
6810         getWheelDelta : function(){
6811             var e = this.browserEvent;
6812             var delta = 0;
6813             if(e.wheelDelta){ /* IE/Opera. */
6814                 delta = e.wheelDelta/120;
6815             }else if(e.detail){ /* Mozilla case. */
6816                 delta = -e.detail/3;
6817             }
6818             return delta;
6819         },
6820
6821         /**
6822          * Returns true if the control, meta, shift or alt key was pressed during this event.
6823          * @return {Boolean}
6824          */
6825         hasModifier : function(){
6826             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6827         },
6828
6829         /**
6830          * Returns true if the target of this event equals el or is a child of el
6831          * @param {String/HTMLElement/Element} el
6832          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6833          * @return {Boolean}
6834          */
6835         within : function(el, related){
6836             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6837             return t && Roo.fly(el).contains(t);
6838         },
6839
6840         getPoint : function(){
6841             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6842         }
6843     };
6844
6845     return new Roo.EventObjectImpl();
6846 }();
6847             
6848     /*
6849  * Based on:
6850  * Ext JS Library 1.1.1
6851  * Copyright(c) 2006-2007, Ext JS, LLC.
6852  *
6853  * Originally Released Under LGPL - original licence link has changed is not relivant.
6854  *
6855  * Fork - LGPL
6856  * <script type="text/javascript">
6857  */
6858
6859  
6860 // was in Composite Element!??!?!
6861  
6862 (function(){
6863     var D = Roo.lib.Dom;
6864     var E = Roo.lib.Event;
6865     var A = Roo.lib.Anim;
6866
6867     // local style camelizing for speed
6868     var propCache = {};
6869     var camelRe = /(-[a-z])/gi;
6870     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6871     var view = document.defaultView;
6872
6873 /**
6874  * @class Roo.Element
6875  * Represents an Element in the DOM.<br><br>
6876  * Usage:<br>
6877 <pre><code>
6878 var el = Roo.get("my-div");
6879
6880 // or with getEl
6881 var el = getEl("my-div");
6882
6883 // or with a DOM element
6884 var el = Roo.get(myDivElement);
6885 </code></pre>
6886  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6887  * each call instead of constructing a new one.<br><br>
6888  * <b>Animations</b><br />
6889  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6890  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6891 <pre>
6892 Option    Default   Description
6893 --------- --------  ---------------------------------------------
6894 duration  .35       The duration of the animation in seconds
6895 easing    easeOut   The YUI easing method
6896 callback  none      A function to execute when the anim completes
6897 scope     this      The scope (this) of the callback function
6898 </pre>
6899 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6900 * manipulate the animation. Here's an example:
6901 <pre><code>
6902 var el = Roo.get("my-div");
6903
6904 // no animation
6905 el.setWidth(100);
6906
6907 // default animation
6908 el.setWidth(100, true);
6909
6910 // animation with some options set
6911 el.setWidth(100, {
6912     duration: 1,
6913     callback: this.foo,
6914     scope: this
6915 });
6916
6917 // using the "anim" property to get the Anim object
6918 var opt = {
6919     duration: 1,
6920     callback: this.foo,
6921     scope: this
6922 };
6923 el.setWidth(100, opt);
6924 ...
6925 if(opt.anim.isAnimated()){
6926     opt.anim.stop();
6927 }
6928 </code></pre>
6929 * <b> Composite (Collections of) Elements</b><br />
6930  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6931  * @constructor Create a new Element directly.
6932  * @param {String/HTMLElement} element
6933  * @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).
6934  */
6935     Roo.Element = function(element, forceNew){
6936         var dom = typeof element == "string" ?
6937                 document.getElementById(element) : element;
6938         if(!dom){ // invalid id/element
6939             return null;
6940         }
6941         var id = dom.id;
6942         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6943             return Roo.Element.cache[id];
6944         }
6945
6946         /**
6947          * The DOM element
6948          * @type HTMLElement
6949          */
6950         this.dom = dom;
6951
6952         /**
6953          * The DOM element ID
6954          * @type String
6955          */
6956         this.id = id || Roo.id(dom);
6957     };
6958
6959     var El = Roo.Element;
6960
6961     El.prototype = {
6962         /**
6963          * The element's default display mode  (defaults to "")
6964          * @type String
6965          */
6966         originalDisplay : "",
6967
6968         visibilityMode : 1,
6969         /**
6970          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6971          * @type String
6972          */
6973         defaultUnit : "px",
6974         /**
6975          * Sets the element's visibility mode. When setVisible() is called it
6976          * will use this to determine whether to set the visibility or the display property.
6977          * @param visMode Element.VISIBILITY or Element.DISPLAY
6978          * @return {Roo.Element} this
6979          */
6980         setVisibilityMode : function(visMode){
6981             this.visibilityMode = visMode;
6982             return this;
6983         },
6984         /**
6985          * Convenience method for setVisibilityMode(Element.DISPLAY)
6986          * @param {String} display (optional) What to set display to when visible
6987          * @return {Roo.Element} this
6988          */
6989         enableDisplayMode : function(display){
6990             this.setVisibilityMode(El.DISPLAY);
6991             if(typeof display != "undefined") this.originalDisplay = display;
6992             return this;
6993         },
6994
6995         /**
6996          * 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)
6997          * @param {String} selector The simple selector to test
6998          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6999                 search as a number or element (defaults to 10 || document.body)
7000          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7001          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7002          */
7003         findParent : function(simpleSelector, maxDepth, returnEl){
7004             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7005             maxDepth = maxDepth || 50;
7006             if(typeof maxDepth != "number"){
7007                 stopEl = Roo.getDom(maxDepth);
7008                 maxDepth = 10;
7009             }
7010             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7011                 if(dq.is(p, simpleSelector)){
7012                     return returnEl ? Roo.get(p) : p;
7013                 }
7014                 depth++;
7015                 p = p.parentNode;
7016             }
7017             return null;
7018         },
7019
7020
7021         /**
7022          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7023          * @param {String} selector The simple selector to test
7024          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7025                 search as a number or element (defaults to 10 || document.body)
7026          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7027          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7028          */
7029         findParentNode : function(simpleSelector, maxDepth, returnEl){
7030             var p = Roo.fly(this.dom.parentNode, '_internal');
7031             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7032         },
7033
7034         /**
7035          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7036          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7037          * @param {String} selector The simple selector to test
7038          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7039                 search as a number or element (defaults to 10 || document.body)
7040          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7041          */
7042         up : function(simpleSelector, maxDepth){
7043             return this.findParentNode(simpleSelector, maxDepth, true);
7044         },
7045
7046
7047
7048         /**
7049          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7050          * @param {String} selector The simple selector to test
7051          * @return {Boolean} True if this element matches the selector, else false
7052          */
7053         is : function(simpleSelector){
7054             return Roo.DomQuery.is(this.dom, simpleSelector);
7055         },
7056
7057         /**
7058          * Perform animation on this element.
7059          * @param {Object} args The YUI animation control args
7060          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7061          * @param {Function} onComplete (optional) Function to call when animation completes
7062          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7063          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7064          * @return {Roo.Element} this
7065          */
7066         animate : function(args, duration, onComplete, easing, animType){
7067             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7068             return this;
7069         },
7070
7071         /*
7072          * @private Internal animation call
7073          */
7074         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7075             animType = animType || 'run';
7076             opt = opt || {};
7077             var anim = Roo.lib.Anim[animType](
7078                 this.dom, args,
7079                 (opt.duration || defaultDur) || .35,
7080                 (opt.easing || defaultEase) || 'easeOut',
7081                 function(){
7082                     Roo.callback(cb, this);
7083                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7084                 },
7085                 this
7086             );
7087             opt.anim = anim;
7088             return anim;
7089         },
7090
7091         // private legacy anim prep
7092         preanim : function(a, i){
7093             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7094         },
7095
7096         /**
7097          * Removes worthless text nodes
7098          * @param {Boolean} forceReclean (optional) By default the element
7099          * keeps track if it has been cleaned already so
7100          * you can call this over and over. However, if you update the element and
7101          * need to force a reclean, you can pass true.
7102          */
7103         clean : function(forceReclean){
7104             if(this.isCleaned && forceReclean !== true){
7105                 return this;
7106             }
7107             var ns = /\S/;
7108             var d = this.dom, n = d.firstChild, ni = -1;
7109             while(n){
7110                 var nx = n.nextSibling;
7111                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7112                     d.removeChild(n);
7113                 }else{
7114                     n.nodeIndex = ++ni;
7115                 }
7116                 n = nx;
7117             }
7118             this.isCleaned = true;
7119             return this;
7120         },
7121
7122         // private
7123         calcOffsetsTo : function(el){
7124             el = Roo.get(el);
7125             var d = el.dom;
7126             var restorePos = false;
7127             if(el.getStyle('position') == 'static'){
7128                 el.position('relative');
7129                 restorePos = true;
7130             }
7131             var x = 0, y =0;
7132             var op = this.dom;
7133             while(op && op != d && op.tagName != 'HTML'){
7134                 x+= op.offsetLeft;
7135                 y+= op.offsetTop;
7136                 op = op.offsetParent;
7137             }
7138             if(restorePos){
7139                 el.position('static');
7140             }
7141             return [x, y];
7142         },
7143
7144         /**
7145          * Scrolls this element into view within the passed container.
7146          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7147          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7148          * @return {Roo.Element} this
7149          */
7150         scrollIntoView : function(container, hscroll){
7151             var c = Roo.getDom(container) || document.body;
7152             var el = this.dom;
7153
7154             var o = this.calcOffsetsTo(c),
7155                 l = o[0],
7156                 t = o[1],
7157                 b = t+el.offsetHeight,
7158                 r = l+el.offsetWidth;
7159
7160             var ch = c.clientHeight;
7161             var ct = parseInt(c.scrollTop, 10);
7162             var cl = parseInt(c.scrollLeft, 10);
7163             var cb = ct + ch;
7164             var cr = cl + c.clientWidth;
7165
7166             if(t < ct){
7167                 c.scrollTop = t;
7168             }else if(b > cb){
7169                 c.scrollTop = b-ch;
7170             }
7171
7172             if(hscroll !== false){
7173                 if(l < cl){
7174                     c.scrollLeft = l;
7175                 }else if(r > cr){
7176                     c.scrollLeft = r-c.clientWidth;
7177                 }
7178             }
7179             return this;
7180         },
7181
7182         // private
7183         scrollChildIntoView : function(child, hscroll){
7184             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7185         },
7186
7187         /**
7188          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7189          * the new height may not be available immediately.
7190          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7191          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7192          * @param {Function} onComplete (optional) Function to call when animation completes
7193          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7194          * @return {Roo.Element} this
7195          */
7196         autoHeight : function(animate, duration, onComplete, easing){
7197             var oldHeight = this.getHeight();
7198             this.clip();
7199             this.setHeight(1); // force clipping
7200             setTimeout(function(){
7201                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7202                 if(!animate){
7203                     this.setHeight(height);
7204                     this.unclip();
7205                     if(typeof onComplete == "function"){
7206                         onComplete();
7207                     }
7208                 }else{
7209                     this.setHeight(oldHeight); // restore original height
7210                     this.setHeight(height, animate, duration, function(){
7211                         this.unclip();
7212                         if(typeof onComplete == "function") onComplete();
7213                     }.createDelegate(this), easing);
7214                 }
7215             }.createDelegate(this), 0);
7216             return this;
7217         },
7218
7219         /**
7220          * Returns true if this element is an ancestor of the passed element
7221          * @param {HTMLElement/String} el The element to check
7222          * @return {Boolean} True if this element is an ancestor of el, else false
7223          */
7224         contains : function(el){
7225             if(!el){return false;}
7226             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7227         },
7228
7229         /**
7230          * Checks whether the element is currently visible using both visibility and display properties.
7231          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7232          * @return {Boolean} True if the element is currently visible, else false
7233          */
7234         isVisible : function(deep) {
7235             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7236             if(deep !== true || !vis){
7237                 return vis;
7238             }
7239             var p = this.dom.parentNode;
7240             while(p && p.tagName.toLowerCase() != "body"){
7241                 if(!Roo.fly(p, '_isVisible').isVisible()){
7242                     return false;
7243                 }
7244                 p = p.parentNode;
7245             }
7246             return true;
7247         },
7248
7249         /**
7250          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7251          * @param {String} selector The CSS selector
7252          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7253          * @return {CompositeElement/CompositeElementLite} The composite element
7254          */
7255         select : function(selector, unique){
7256             return El.select(selector, unique, this.dom);
7257         },
7258
7259         /**
7260          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7261          * @param {String} selector The CSS selector
7262          * @return {Array} An array of the matched nodes
7263          */
7264         query : function(selector, unique){
7265             return Roo.DomQuery.select(selector, this.dom);
7266         },
7267
7268         /**
7269          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7270          * @param {String} selector The CSS selector
7271          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7272          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7273          */
7274         child : function(selector, returnDom){
7275             var n = Roo.DomQuery.selectNode(selector, this.dom);
7276             return returnDom ? n : Roo.get(n);
7277         },
7278
7279         /**
7280          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7281          * @param {String} selector The CSS selector
7282          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7283          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7284          */
7285         down : function(selector, returnDom){
7286             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7287             return returnDom ? n : Roo.get(n);
7288         },
7289
7290         /**
7291          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7292          * @param {String} group The group the DD object is member of
7293          * @param {Object} config The DD config object
7294          * @param {Object} overrides An object containing methods to override/implement on the DD object
7295          * @return {Roo.dd.DD} The DD object
7296          */
7297         initDD : function(group, config, overrides){
7298             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7299             return Roo.apply(dd, overrides);
7300         },
7301
7302         /**
7303          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7304          * @param {String} group The group the DDProxy object is member of
7305          * @param {Object} config The DDProxy config object
7306          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7307          * @return {Roo.dd.DDProxy} The DDProxy object
7308          */
7309         initDDProxy : function(group, config, overrides){
7310             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7311             return Roo.apply(dd, overrides);
7312         },
7313
7314         /**
7315          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7316          * @param {String} group The group the DDTarget object is member of
7317          * @param {Object} config The DDTarget config object
7318          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7319          * @return {Roo.dd.DDTarget} The DDTarget object
7320          */
7321         initDDTarget : function(group, config, overrides){
7322             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7323             return Roo.apply(dd, overrides);
7324         },
7325
7326         /**
7327          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7328          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7329          * @param {Boolean} visible Whether the element is visible
7330          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7331          * @return {Roo.Element} this
7332          */
7333          setVisible : function(visible, animate){
7334             if(!animate || !A){
7335                 if(this.visibilityMode == El.DISPLAY){
7336                     this.setDisplayed(visible);
7337                 }else{
7338                     this.fixDisplay();
7339                     this.dom.style.visibility = visible ? "visible" : "hidden";
7340                 }
7341             }else{
7342                 // closure for composites
7343                 var dom = this.dom;
7344                 var visMode = this.visibilityMode;
7345                 if(visible){
7346                     this.setOpacity(.01);
7347                     this.setVisible(true);
7348                 }
7349                 this.anim({opacity: { to: (visible?1:0) }},
7350                       this.preanim(arguments, 1),
7351                       null, .35, 'easeIn', function(){
7352                          if(!visible){
7353                              if(visMode == El.DISPLAY){
7354                                  dom.style.display = "none";
7355                              }else{
7356                                  dom.style.visibility = "hidden";
7357                              }
7358                              Roo.get(dom).setOpacity(1);
7359                          }
7360                      });
7361             }
7362             return this;
7363         },
7364
7365         /**
7366          * Returns true if display is not "none"
7367          * @return {Boolean}
7368          */
7369         isDisplayed : function() {
7370             return this.getStyle("display") != "none";
7371         },
7372
7373         /**
7374          * Toggles the element's visibility or display, depending on visibility mode.
7375          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7376          * @return {Roo.Element} this
7377          */
7378         toggle : function(animate){
7379             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7380             return this;
7381         },
7382
7383         /**
7384          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7385          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7386          * @return {Roo.Element} this
7387          */
7388         setDisplayed : function(value) {
7389             if(typeof value == "boolean"){
7390                value = value ? this.originalDisplay : "none";
7391             }
7392             this.setStyle("display", value);
7393             return this;
7394         },
7395
7396         /**
7397          * Tries to focus the element. Any exceptions are caught and ignored.
7398          * @return {Roo.Element} this
7399          */
7400         focus : function() {
7401             try{
7402                 this.dom.focus();
7403             }catch(e){}
7404             return this;
7405         },
7406
7407         /**
7408          * Tries to blur the element. Any exceptions are caught and ignored.
7409          * @return {Roo.Element} this
7410          */
7411         blur : function() {
7412             try{
7413                 this.dom.blur();
7414             }catch(e){}
7415             return this;
7416         },
7417
7418         /**
7419          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7420          * @param {String/Array} className The CSS class to add, or an array of classes
7421          * @return {Roo.Element} this
7422          */
7423         addClass : function(className){
7424             if(className instanceof Array){
7425                 for(var i = 0, len = className.length; i < len; i++) {
7426                     this.addClass(className[i]);
7427                 }
7428             }else{
7429                 if(className && !this.hasClass(className)){
7430                     this.dom.className = this.dom.className + " " + className;
7431                 }
7432             }
7433             return this;
7434         },
7435
7436         /**
7437          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7438          * @param {String/Array} className The CSS class to add, or an array of classes
7439          * @return {Roo.Element} this
7440          */
7441         radioClass : function(className){
7442             var siblings = this.dom.parentNode.childNodes;
7443             for(var i = 0; i < siblings.length; i++) {
7444                 var s = siblings[i];
7445                 if(s.nodeType == 1){
7446                     Roo.get(s).removeClass(className);
7447                 }
7448             }
7449             this.addClass(className);
7450             return this;
7451         },
7452
7453         /**
7454          * Removes one or more CSS classes from the element.
7455          * @param {String/Array} className The CSS class to remove, or an array of classes
7456          * @return {Roo.Element} this
7457          */
7458         removeClass : function(className){
7459             if(!className || !this.dom.className){
7460                 return this;
7461             }
7462             if(className instanceof Array){
7463                 for(var i = 0, len = className.length; i < len; i++) {
7464                     this.removeClass(className[i]);
7465                 }
7466             }else{
7467                 if(this.hasClass(className)){
7468                     var re = this.classReCache[className];
7469                     if (!re) {
7470                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7471                        this.classReCache[className] = re;
7472                     }
7473                     this.dom.className =
7474                         this.dom.className.replace(re, " ");
7475                 }
7476             }
7477             return this;
7478         },
7479
7480         // private
7481         classReCache: {},
7482
7483         /**
7484          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7485          * @param {String} className The CSS class to toggle
7486          * @return {Roo.Element} this
7487          */
7488         toggleClass : function(className){
7489             if(this.hasClass(className)){
7490                 this.removeClass(className);
7491             }else{
7492                 this.addClass(className);
7493             }
7494             return this;
7495         },
7496
7497         /**
7498          * Checks if the specified CSS class exists on this element's DOM node.
7499          * @param {String} className The CSS class to check for
7500          * @return {Boolean} True if the class exists, else false
7501          */
7502         hasClass : function(className){
7503             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7504         },
7505
7506         /**
7507          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7508          * @param {String} oldClassName The CSS class to replace
7509          * @param {String} newClassName The replacement CSS class
7510          * @return {Roo.Element} this
7511          */
7512         replaceClass : function(oldClassName, newClassName){
7513             this.removeClass(oldClassName);
7514             this.addClass(newClassName);
7515             return this;
7516         },
7517
7518         /**
7519          * Returns an object with properties matching the styles requested.
7520          * For example, el.getStyles('color', 'font-size', 'width') might return
7521          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7522          * @param {String} style1 A style name
7523          * @param {String} style2 A style name
7524          * @param {String} etc.
7525          * @return {Object} The style object
7526          */
7527         getStyles : function(){
7528             var a = arguments, len = a.length, r = {};
7529             for(var i = 0; i < len; i++){
7530                 r[a[i]] = this.getStyle(a[i]);
7531             }
7532             return r;
7533         },
7534
7535         /**
7536          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7537          * @param {String} property The style property whose value is returned.
7538          * @return {String} The current value of the style property for this element.
7539          */
7540         getStyle : function(){
7541             return view && view.getComputedStyle ?
7542                 function(prop){
7543                     var el = this.dom, v, cs, camel;
7544                     if(prop == 'float'){
7545                         prop = "cssFloat";
7546                     }
7547                     if(el.style && (v = el.style[prop])){
7548                         return v;
7549                     }
7550                     if(cs = view.getComputedStyle(el, "")){
7551                         if(!(camel = propCache[prop])){
7552                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7553                         }
7554                         return cs[camel];
7555                     }
7556                     return null;
7557                 } :
7558                 function(prop){
7559                     var el = this.dom, v, cs, camel;
7560                     if(prop == 'opacity'){
7561                         if(typeof el.style.filter == 'string'){
7562                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7563                             if(m){
7564                                 var fv = parseFloat(m[1]);
7565                                 if(!isNaN(fv)){
7566                                     return fv ? fv / 100 : 0;
7567                                 }
7568                             }
7569                         }
7570                         return 1;
7571                     }else if(prop == 'float'){
7572                         prop = "styleFloat";
7573                     }
7574                     if(!(camel = propCache[prop])){
7575                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7576                     }
7577                     if(v = el.style[camel]){
7578                         return v;
7579                     }
7580                     if(cs = el.currentStyle){
7581                         return cs[camel];
7582                     }
7583                     return null;
7584                 };
7585         }(),
7586
7587         /**
7588          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7589          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7590          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7591          * @return {Roo.Element} this
7592          */
7593         setStyle : function(prop, value){
7594             if(typeof prop == "string"){
7595                 
7596                 if (prop == 'float') {
7597                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7598                     return this;
7599                 }
7600                 
7601                 var camel;
7602                 if(!(camel = propCache[prop])){
7603                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7604                 }
7605                 
7606                 if(camel == 'opacity') {
7607                     this.setOpacity(value);
7608                 }else{
7609                     this.dom.style[camel] = value;
7610                 }
7611             }else{
7612                 for(var style in prop){
7613                     if(typeof prop[style] != "function"){
7614                        this.setStyle(style, prop[style]);
7615                     }
7616                 }
7617             }
7618             return this;
7619         },
7620
7621         /**
7622          * More flexible version of {@link #setStyle} for setting style properties.
7623          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7624          * a function which returns such a specification.
7625          * @return {Roo.Element} this
7626          */
7627         applyStyles : function(style){
7628             Roo.DomHelper.applyStyles(this.dom, style);
7629             return this;
7630         },
7631
7632         /**
7633           * 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).
7634           * @return {Number} The X position of the element
7635           */
7636         getX : function(){
7637             return D.getX(this.dom);
7638         },
7639
7640         /**
7641           * 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).
7642           * @return {Number} The Y position of the element
7643           */
7644         getY : function(){
7645             return D.getY(this.dom);
7646         },
7647
7648         /**
7649           * 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).
7650           * @return {Array} The XY position of the element
7651           */
7652         getXY : function(){
7653             return D.getXY(this.dom);
7654         },
7655
7656         /**
7657          * 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).
7658          * @param {Number} The X position of the element
7659          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7660          * @return {Roo.Element} this
7661          */
7662         setX : function(x, animate){
7663             if(!animate || !A){
7664                 D.setX(this.dom, x);
7665             }else{
7666                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7667             }
7668             return this;
7669         },
7670
7671         /**
7672          * 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).
7673          * @param {Number} The Y position of the element
7674          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7675          * @return {Roo.Element} this
7676          */
7677         setY : function(y, animate){
7678             if(!animate || !A){
7679                 D.setY(this.dom, y);
7680             }else{
7681                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7682             }
7683             return this;
7684         },
7685
7686         /**
7687          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7688          * @param {String} left The left CSS property value
7689          * @return {Roo.Element} this
7690          */
7691         setLeft : function(left){
7692             this.setStyle("left", this.addUnits(left));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7698          * @param {String} top The top CSS property value
7699          * @return {Roo.Element} this
7700          */
7701         setTop : function(top){
7702             this.setStyle("top", this.addUnits(top));
7703             return this;
7704         },
7705
7706         /**
7707          * Sets the element's CSS right style.
7708          * @param {String} right The right CSS property value
7709          * @return {Roo.Element} this
7710          */
7711         setRight : function(right){
7712             this.setStyle("right", this.addUnits(right));
7713             return this;
7714         },
7715
7716         /**
7717          * Sets the element's CSS bottom style.
7718          * @param {String} bottom The bottom CSS property value
7719          * @return {Roo.Element} this
7720          */
7721         setBottom : function(bottom){
7722             this.setStyle("bottom", this.addUnits(bottom));
7723             return this;
7724         },
7725
7726         /**
7727          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7728          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7729          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7730          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7731          * @return {Roo.Element} this
7732          */
7733         setXY : function(pos, animate){
7734             if(!animate || !A){
7735                 D.setXY(this.dom, pos);
7736             }else{
7737                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7738             }
7739             return this;
7740         },
7741
7742         /**
7743          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7744          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7745          * @param {Number} x X value for new position (coordinates are page-based)
7746          * @param {Number} y Y value for new position (coordinates are page-based)
7747          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7748          * @return {Roo.Element} this
7749          */
7750         setLocation : function(x, y, animate){
7751             this.setXY([x, y], this.preanim(arguments, 2));
7752             return this;
7753         },
7754
7755         /**
7756          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7757          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7758          * @param {Number} x X value for new position (coordinates are page-based)
7759          * @param {Number} y Y value for new position (coordinates are page-based)
7760          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7761          * @return {Roo.Element} this
7762          */
7763         moveTo : function(x, y, animate){
7764             this.setXY([x, y], this.preanim(arguments, 2));
7765             return this;
7766         },
7767
7768         /**
7769          * Returns the region of the given element.
7770          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7771          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7772          */
7773         getRegion : function(){
7774             return D.getRegion(this.dom);
7775         },
7776
7777         /**
7778          * Returns the offset height of the element
7779          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7780          * @return {Number} The element's height
7781          */
7782         getHeight : function(contentHeight){
7783             var h = this.dom.offsetHeight || 0;
7784             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7785         },
7786
7787         /**
7788          * Returns the offset width of the element
7789          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7790          * @return {Number} The element's width
7791          */
7792         getWidth : function(contentWidth){
7793             var w = this.dom.offsetWidth || 0;
7794             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7795         },
7796
7797         /**
7798          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7799          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7800          * if a height has not been set using CSS.
7801          * @return {Number}
7802          */
7803         getComputedHeight : function(){
7804             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7805             if(!h){
7806                 h = parseInt(this.getStyle('height'), 10) || 0;
7807                 if(!this.isBorderBox()){
7808                     h += this.getFrameWidth('tb');
7809                 }
7810             }
7811             return h;
7812         },
7813
7814         /**
7815          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7816          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7817          * if a width has not been set using CSS.
7818          * @return {Number}
7819          */
7820         getComputedWidth : function(){
7821             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7822             if(!w){
7823                 w = parseInt(this.getStyle('width'), 10) || 0;
7824                 if(!this.isBorderBox()){
7825                     w += this.getFrameWidth('lr');
7826                 }
7827             }
7828             return w;
7829         },
7830
7831         /**
7832          * Returns the size of the element.
7833          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7834          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7835          */
7836         getSize : function(contentSize){
7837             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7838         },
7839
7840         /**
7841          * Returns the width and height of the viewport.
7842          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7843          */
7844         getViewSize : function(){
7845             var d = this.dom, doc = document, aw = 0, ah = 0;
7846             if(d == doc || d == doc.body){
7847                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7848             }else{
7849                 return {
7850                     width : d.clientWidth,
7851                     height: d.clientHeight
7852                 };
7853             }
7854         },
7855
7856         /**
7857          * Returns the value of the "value" attribute
7858          * @param {Boolean} asNumber true to parse the value as a number
7859          * @return {String/Number}
7860          */
7861         getValue : function(asNumber){
7862             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7863         },
7864
7865         // private
7866         adjustWidth : function(width){
7867             if(typeof width == "number"){
7868                 if(this.autoBoxAdjust && !this.isBorderBox()){
7869                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7870                 }
7871                 if(width < 0){
7872                     width = 0;
7873                 }
7874             }
7875             return width;
7876         },
7877
7878         // private
7879         adjustHeight : function(height){
7880             if(typeof height == "number"){
7881                if(this.autoBoxAdjust && !this.isBorderBox()){
7882                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7883                }
7884                if(height < 0){
7885                    height = 0;
7886                }
7887             }
7888             return height;
7889         },
7890
7891         /**
7892          * Set the width of the element
7893          * @param {Number} width The new width
7894          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7895          * @return {Roo.Element} this
7896          */
7897         setWidth : function(width, animate){
7898             width = this.adjustWidth(width);
7899             if(!animate || !A){
7900                 this.dom.style.width = this.addUnits(width);
7901             }else{
7902                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7903             }
7904             return this;
7905         },
7906
7907         /**
7908          * Set the height of the element
7909          * @param {Number} height The new height
7910          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7911          * @return {Roo.Element} this
7912          */
7913          setHeight : function(height, animate){
7914             height = this.adjustHeight(height);
7915             if(!animate || !A){
7916                 this.dom.style.height = this.addUnits(height);
7917             }else{
7918                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7919             }
7920             return this;
7921         },
7922
7923         /**
7924          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7925          * @param {Number} width The new width
7926          * @param {Number} height The new height
7927          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7928          * @return {Roo.Element} this
7929          */
7930          setSize : function(width, height, animate){
7931             if(typeof width == "object"){ // in case of object from getSize()
7932                 height = width.height; width = width.width;
7933             }
7934             width = this.adjustWidth(width); height = this.adjustHeight(height);
7935             if(!animate || !A){
7936                 this.dom.style.width = this.addUnits(width);
7937                 this.dom.style.height = this.addUnits(height);
7938             }else{
7939                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7940             }
7941             return this;
7942         },
7943
7944         /**
7945          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7946          * @param {Number} x X value for new position (coordinates are page-based)
7947          * @param {Number} y Y value for new position (coordinates are page-based)
7948          * @param {Number} width The new width
7949          * @param {Number} height The new height
7950          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7951          * @return {Roo.Element} this
7952          */
7953         setBounds : function(x, y, width, height, animate){
7954             if(!animate || !A){
7955                 this.setSize(width, height);
7956                 this.setLocation(x, y);
7957             }else{
7958                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7959                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7960                               this.preanim(arguments, 4), 'motion');
7961             }
7962             return this;
7963         },
7964
7965         /**
7966          * 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.
7967          * @param {Roo.lib.Region} region The region to fill
7968          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7969          * @return {Roo.Element} this
7970          */
7971         setRegion : function(region, animate){
7972             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7973             return this;
7974         },
7975
7976         /**
7977          * Appends an event handler
7978          *
7979          * @param {String}   eventName     The type of event to append
7980          * @param {Function} fn        The method the event invokes
7981          * @param {Object} scope       (optional) The scope (this object) of the fn
7982          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7983          */
7984         addListener : function(eventName, fn, scope, options){
7985             if (this.dom) {
7986                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7987             }
7988         },
7989
7990         /**
7991          * Removes an event handler from this element
7992          * @param {String} eventName the type of event to remove
7993          * @param {Function} fn the method the event invokes
7994          * @return {Roo.Element} this
7995          */
7996         removeListener : function(eventName, fn){
7997             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7998             return this;
7999         },
8000
8001         /**
8002          * Removes all previous added listeners from this element
8003          * @return {Roo.Element} this
8004          */
8005         removeAllListeners : function(){
8006             E.purgeElement(this.dom);
8007             return this;
8008         },
8009
8010         relayEvent : function(eventName, observable){
8011             this.on(eventName, function(e){
8012                 observable.fireEvent(eventName, e);
8013             });
8014         },
8015
8016         /**
8017          * Set the opacity of the element
8018          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8019          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8020          * @return {Roo.Element} this
8021          */
8022          setOpacity : function(opacity, animate){
8023             if(!animate || !A){
8024                 var s = this.dom.style;
8025                 if(Roo.isIE){
8026                     s.zoom = 1;
8027                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8028                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8029                 }else{
8030                     s.opacity = opacity;
8031                 }
8032             }else{
8033                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8034             }
8035             return this;
8036         },
8037
8038         /**
8039          * Gets the left X coordinate
8040          * @param {Boolean} local True to get the local css position instead of page coordinate
8041          * @return {Number}
8042          */
8043         getLeft : function(local){
8044             if(!local){
8045                 return this.getX();
8046             }else{
8047                 return parseInt(this.getStyle("left"), 10) || 0;
8048             }
8049         },
8050
8051         /**
8052          * Gets the right X coordinate of the element (element X position + element width)
8053          * @param {Boolean} local True to get the local css position instead of page coordinate
8054          * @return {Number}
8055          */
8056         getRight : function(local){
8057             if(!local){
8058                 return this.getX() + this.getWidth();
8059             }else{
8060                 return (this.getLeft(true) + this.getWidth()) || 0;
8061             }
8062         },
8063
8064         /**
8065          * Gets the top Y coordinate
8066          * @param {Boolean} local True to get the local css position instead of page coordinate
8067          * @return {Number}
8068          */
8069         getTop : function(local) {
8070             if(!local){
8071                 return this.getY();
8072             }else{
8073                 return parseInt(this.getStyle("top"), 10) || 0;
8074             }
8075         },
8076
8077         /**
8078          * Gets the bottom Y coordinate of the element (element Y position + element height)
8079          * @param {Boolean} local True to get the local css position instead of page coordinate
8080          * @return {Number}
8081          */
8082         getBottom : function(local){
8083             if(!local){
8084                 return this.getY() + this.getHeight();
8085             }else{
8086                 return (this.getTop(true) + this.getHeight()) || 0;
8087             }
8088         },
8089
8090         /**
8091         * Initializes positioning on this element. If a desired position is not passed, it will make the
8092         * the element positioned relative IF it is not already positioned.
8093         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8094         * @param {Number} zIndex (optional) The zIndex to apply
8095         * @param {Number} x (optional) Set the page X position
8096         * @param {Number} y (optional) Set the page Y position
8097         */
8098         position : function(pos, zIndex, x, y){
8099             if(!pos){
8100                if(this.getStyle('position') == 'static'){
8101                    this.setStyle('position', 'relative');
8102                }
8103             }else{
8104                 this.setStyle("position", pos);
8105             }
8106             if(zIndex){
8107                 this.setStyle("z-index", zIndex);
8108             }
8109             if(x !== undefined && y !== undefined){
8110                 this.setXY([x, y]);
8111             }else if(x !== undefined){
8112                 this.setX(x);
8113             }else if(y !== undefined){
8114                 this.setY(y);
8115             }
8116         },
8117
8118         /**
8119         * Clear positioning back to the default when the document was loaded
8120         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8121         * @return {Roo.Element} this
8122          */
8123         clearPositioning : function(value){
8124             value = value ||'';
8125             this.setStyle({
8126                 "left": value,
8127                 "right": value,
8128                 "top": value,
8129                 "bottom": value,
8130                 "z-index": "",
8131                 "position" : "static"
8132             });
8133             return this;
8134         },
8135
8136         /**
8137         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8138         * snapshot before performing an update and then restoring the element.
8139         * @return {Object}
8140         */
8141         getPositioning : function(){
8142             var l = this.getStyle("left");
8143             var t = this.getStyle("top");
8144             return {
8145                 "position" : this.getStyle("position"),
8146                 "left" : l,
8147                 "right" : l ? "" : this.getStyle("right"),
8148                 "top" : t,
8149                 "bottom" : t ? "" : this.getStyle("bottom"),
8150                 "z-index" : this.getStyle("z-index")
8151             };
8152         },
8153
8154         /**
8155          * Gets the width of the border(s) for the specified side(s)
8156          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8157          * passing lr would get the border (l)eft width + the border (r)ight width.
8158          * @return {Number} The width of the sides passed added together
8159          */
8160         getBorderWidth : function(side){
8161             return this.addStyles(side, El.borders);
8162         },
8163
8164         /**
8165          * Gets the width of the padding(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 padding (l)eft + the padding (r)ight.
8168          * @return {Number} The padding of the sides passed added together
8169          */
8170         getPadding : function(side){
8171             return this.addStyles(side, El.paddings);
8172         },
8173
8174         /**
8175         * Set positioning with an object returned by getPositioning().
8176         * @param {Object} posCfg
8177         * @return {Roo.Element} this
8178          */
8179         setPositioning : function(pc){
8180             this.applyStyles(pc);
8181             if(pc.right == "auto"){
8182                 this.dom.style.right = "";
8183             }
8184             if(pc.bottom == "auto"){
8185                 this.dom.style.bottom = "";
8186             }
8187             return this;
8188         },
8189
8190         // private
8191         fixDisplay : function(){
8192             if(this.getStyle("display") == "none"){
8193                 this.setStyle("visibility", "hidden");
8194                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8195                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8196                     this.setStyle("display", "block");
8197                 }
8198             }
8199         },
8200
8201         /**
8202          * Quick set left and top adding default units
8203          * @param {String} left The left CSS property value
8204          * @param {String} top The top CSS property value
8205          * @return {Roo.Element} this
8206          */
8207          setLeftTop : function(left, top){
8208             this.dom.style.left = this.addUnits(left);
8209             this.dom.style.top = this.addUnits(top);
8210             return this;
8211         },
8212
8213         /**
8214          * Move this element relative to its current position.
8215          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8216          * @param {Number} distance How far to move the element in pixels
8217          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8218          * @return {Roo.Element} this
8219          */
8220          move : function(direction, distance, animate){
8221             var xy = this.getXY();
8222             direction = direction.toLowerCase();
8223             switch(direction){
8224                 case "l":
8225                 case "left":
8226                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8227                     break;
8228                case "r":
8229                case "right":
8230                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8231                     break;
8232                case "t":
8233                case "top":
8234                case "up":
8235                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8236                     break;
8237                case "b":
8238                case "bottom":
8239                case "down":
8240                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8241                     break;
8242             }
8243             return this;
8244         },
8245
8246         /**
8247          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8248          * @return {Roo.Element} this
8249          */
8250         clip : function(){
8251             if(!this.isClipped){
8252                this.isClipped = true;
8253                this.originalClip = {
8254                    "o": this.getStyle("overflow"),
8255                    "x": this.getStyle("overflow-x"),
8256                    "y": this.getStyle("overflow-y")
8257                };
8258                this.setStyle("overflow", "hidden");
8259                this.setStyle("overflow-x", "hidden");
8260                this.setStyle("overflow-y", "hidden");
8261             }
8262             return this;
8263         },
8264
8265         /**
8266          *  Return clipping (overflow) to original clipping before clip() was called
8267          * @return {Roo.Element} this
8268          */
8269         unclip : function(){
8270             if(this.isClipped){
8271                 this.isClipped = false;
8272                 var o = this.originalClip;
8273                 if(o.o){this.setStyle("overflow", o.o);}
8274                 if(o.x){this.setStyle("overflow-x", o.x);}
8275                 if(o.y){this.setStyle("overflow-y", o.y);}
8276             }
8277             return this;
8278         },
8279
8280
8281         /**
8282          * Gets the x,y coordinates specified by the anchor position on the element.
8283          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8284          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8285          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8286          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8287          * @return {Array} [x, y] An array containing the element's x and y coordinates
8288          */
8289         getAnchorXY : function(anchor, local, s){
8290             //Passing a different size is useful for pre-calculating anchors,
8291             //especially for anchored animations that change the el size.
8292
8293             var w, h, vp = false;
8294             if(!s){
8295                 var d = this.dom;
8296                 if(d == document.body || d == document){
8297                     vp = true;
8298                     w = D.getViewWidth(); h = D.getViewHeight();
8299                 }else{
8300                     w = this.getWidth(); h = this.getHeight();
8301                 }
8302             }else{
8303                 w = s.width;  h = s.height;
8304             }
8305             var x = 0, y = 0, r = Math.round;
8306             switch((anchor || "tl").toLowerCase()){
8307                 case "c":
8308                     x = r(w*.5);
8309                     y = r(h*.5);
8310                 break;
8311                 case "t":
8312                     x = r(w*.5);
8313                     y = 0;
8314                 break;
8315                 case "l":
8316                     x = 0;
8317                     y = r(h*.5);
8318                 break;
8319                 case "r":
8320                     x = w;
8321                     y = r(h*.5);
8322                 break;
8323                 case "b":
8324                     x = r(w*.5);
8325                     y = h;
8326                 break;
8327                 case "tl":
8328                     x = 0;
8329                     y = 0;
8330                 break;
8331                 case "bl":
8332                     x = 0;
8333                     y = h;
8334                 break;
8335                 case "br":
8336                     x = w;
8337                     y = h;
8338                 break;
8339                 case "tr":
8340                     x = w;
8341                     y = 0;
8342                 break;
8343             }
8344             if(local === true){
8345                 return [x, y];
8346             }
8347             if(vp){
8348                 var sc = this.getScroll();
8349                 return [x + sc.left, y + sc.top];
8350             }
8351             //Add the element's offset xy
8352             var o = this.getXY();
8353             return [x+o[0], y+o[1]];
8354         },
8355
8356         /**
8357          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8358          * supported position values.
8359          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8360          * @param {String} position The position to align to.
8361          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8362          * @return {Array} [x, y]
8363          */
8364         getAlignToXY : function(el, p, o){
8365             el = Roo.get(el);
8366             var d = this.dom;
8367             if(!el.dom){
8368                 throw "Element.alignTo with an element that doesn't exist";
8369             }
8370             var c = false; //constrain to viewport
8371             var p1 = "", p2 = "";
8372             o = o || [0,0];
8373
8374             if(!p){
8375                 p = "tl-bl";
8376             }else if(p == "?"){
8377                 p = "tl-bl?";
8378             }else if(p.indexOf("-") == -1){
8379                 p = "tl-" + p;
8380             }
8381             p = p.toLowerCase();
8382             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8383             if(!m){
8384                throw "Element.alignTo with an invalid alignment " + p;
8385             }
8386             p1 = m[1]; p2 = m[2]; c = !!m[3];
8387
8388             //Subtract the aligned el's internal xy from the target's offset xy
8389             //plus custom offset to get the aligned el's new offset xy
8390             var a1 = this.getAnchorXY(p1, true);
8391             var a2 = el.getAnchorXY(p2, false);
8392             var x = a2[0] - a1[0] + o[0];
8393             var y = a2[1] - a1[1] + o[1];
8394             if(c){
8395                 //constrain the aligned el to viewport if necessary
8396                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8397                 // 5px of margin for ie
8398                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8399
8400                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8401                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8402                 //otherwise swap the aligned el to the opposite border of the target.
8403                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8404                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8405                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8406                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8407
8408                var doc = document;
8409                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8410                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8411
8412                if((x+w) > dw + scrollX){
8413                     x = swapX ? r.left-w : dw+scrollX-w;
8414                 }
8415                if(x < scrollX){
8416                    x = swapX ? r.right : scrollX;
8417                }
8418                if((y+h) > dh + scrollY){
8419                     y = swapY ? r.top-h : dh+scrollY-h;
8420                 }
8421                if (y < scrollY){
8422                    y = swapY ? r.bottom : scrollY;
8423                }
8424             }
8425             return [x,y];
8426         },
8427
8428         // private
8429         getConstrainToXY : function(){
8430             var os = {top:0, left:0, bottom:0, right: 0};
8431
8432             return function(el, local, offsets, proposedXY){
8433                 el = Roo.get(el);
8434                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8435
8436                 var vw, vh, vx = 0, vy = 0;
8437                 if(el.dom == document.body || el.dom == document){
8438                     vw = Roo.lib.Dom.getViewWidth();
8439                     vh = Roo.lib.Dom.getViewHeight();
8440                 }else{
8441                     vw = el.dom.clientWidth;
8442                     vh = el.dom.clientHeight;
8443                     if(!local){
8444                         var vxy = el.getXY();
8445                         vx = vxy[0];
8446                         vy = vxy[1];
8447                     }
8448                 }
8449
8450                 var s = el.getScroll();
8451
8452                 vx += offsets.left + s.left;
8453                 vy += offsets.top + s.top;
8454
8455                 vw -= offsets.right;
8456                 vh -= offsets.bottom;
8457
8458                 var vr = vx+vw;
8459                 var vb = vy+vh;
8460
8461                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8462                 var x = xy[0], y = xy[1];
8463                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8464
8465                 // only move it if it needs it
8466                 var moved = false;
8467
8468                 // first validate right/bottom
8469                 if((x + w) > vr){
8470                     x = vr - w;
8471                     moved = true;
8472                 }
8473                 if((y + h) > vb){
8474                     y = vb - h;
8475                     moved = true;
8476                 }
8477                 // then make sure top/left isn't negative
8478                 if(x < vx){
8479                     x = vx;
8480                     moved = true;
8481                 }
8482                 if(y < vy){
8483                     y = vy;
8484                     moved = true;
8485                 }
8486                 return moved ? [x, y] : false;
8487             };
8488         }(),
8489
8490         // private
8491         adjustForConstraints : function(xy, parent, offsets){
8492             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8493         },
8494
8495         /**
8496          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8497          * document it aligns it to the viewport.
8498          * The position parameter is optional, and can be specified in any one of the following formats:
8499          * <ul>
8500          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8501          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8502          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8503          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8504          *   <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
8505          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8506          * </ul>
8507          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8508          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8509          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8510          * that specified in order to enforce the viewport constraints.
8511          * Following are all of the supported anchor positions:
8512     <pre>
8513     Value  Description
8514     -----  -----------------------------
8515     tl     The top left corner (default)
8516     t      The center of the top edge
8517     tr     The top right corner
8518     l      The center of the left edge
8519     c      In the center of the element
8520     r      The center of the right edge
8521     bl     The bottom left corner
8522     b      The center of the bottom edge
8523     br     The bottom right corner
8524     </pre>
8525     Example Usage:
8526     <pre><code>
8527     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8528     el.alignTo("other-el");
8529
8530     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8531     el.alignTo("other-el", "tr?");
8532
8533     // align the bottom right corner of el with the center left edge of other-el
8534     el.alignTo("other-el", "br-l?");
8535
8536     // align the center of el with the bottom left corner of other-el and
8537     // adjust the x position by -6 pixels (and the y position by 0)
8538     el.alignTo("other-el", "c-bl", [-6, 0]);
8539     </code></pre>
8540          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8541          * @param {String} position The position to align to.
8542          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8543          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8544          * @return {Roo.Element} this
8545          */
8546         alignTo : function(element, position, offsets, animate){
8547             var xy = this.getAlignToXY(element, position, offsets);
8548             this.setXY(xy, this.preanim(arguments, 3));
8549             return this;
8550         },
8551
8552         /**
8553          * Anchors an element to another element and realigns it when the window is resized.
8554          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8555          * @param {String} position The position to align to.
8556          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8557          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8558          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8559          * is a number, it is used as the buffer delay (defaults to 50ms).
8560          * @param {Function} callback The function to call after the animation finishes
8561          * @return {Roo.Element} this
8562          */
8563         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8564             var action = function(){
8565                 this.alignTo(el, alignment, offsets, animate);
8566                 Roo.callback(callback, this);
8567             };
8568             Roo.EventManager.onWindowResize(action, this);
8569             var tm = typeof monitorScroll;
8570             if(tm != 'undefined'){
8571                 Roo.EventManager.on(window, 'scroll', action, this,
8572                     {buffer: tm == 'number' ? monitorScroll : 50});
8573             }
8574             action.call(this); // align immediately
8575             return this;
8576         },
8577         /**
8578          * Clears any opacity settings from this element. Required in some cases for IE.
8579          * @return {Roo.Element} this
8580          */
8581         clearOpacity : function(){
8582             if (window.ActiveXObject) {
8583                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8584                     this.dom.style.filter = "";
8585                 }
8586             } else {
8587                 this.dom.style.opacity = "";
8588                 this.dom.style["-moz-opacity"] = "";
8589                 this.dom.style["-khtml-opacity"] = "";
8590             }
8591             return this;
8592         },
8593
8594         /**
8595          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8596          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8597          * @return {Roo.Element} this
8598          */
8599         hide : function(animate){
8600             this.setVisible(false, this.preanim(arguments, 0));
8601             return this;
8602         },
8603
8604         /**
8605         * Show 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         show : function(animate){
8610             this.setVisible(true, this.preanim(arguments, 0));
8611             return this;
8612         },
8613
8614         /**
8615          * @private Test if size has a unit, otherwise appends the default
8616          */
8617         addUnits : function(size){
8618             return Roo.Element.addUnits(size, this.defaultUnit);
8619         },
8620
8621         /**
8622          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8623          * @return {Roo.Element} this
8624          */
8625         beginMeasure : function(){
8626             var el = this.dom;
8627             if(el.offsetWidth || el.offsetHeight){
8628                 return this; // offsets work already
8629             }
8630             var changed = [];
8631             var p = this.dom, b = document.body; // start with this element
8632             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8633                 var pe = Roo.get(p);
8634                 if(pe.getStyle('display') == 'none'){
8635                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8636                     p.style.visibility = "hidden";
8637                     p.style.display = "block";
8638                 }
8639                 p = p.parentNode;
8640             }
8641             this._measureChanged = changed;
8642             return this;
8643
8644         },
8645
8646         /**
8647          * Restores displays to before beginMeasure was called
8648          * @return {Roo.Element} this
8649          */
8650         endMeasure : function(){
8651             var changed = this._measureChanged;
8652             if(changed){
8653                 for(var i = 0, len = changed.length; i < len; i++) {
8654                     var r = changed[i];
8655                     r.el.style.visibility = r.visibility;
8656                     r.el.style.display = "none";
8657                 }
8658                 this._measureChanged = null;
8659             }
8660             return this;
8661         },
8662
8663         /**
8664         * Update the innerHTML of this element, optionally searching for and processing scripts
8665         * @param {String} html The new HTML
8666         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8667         * @param {Function} callback For async script loading you can be noticed when the update completes
8668         * @return {Roo.Element} this
8669          */
8670         update : function(html, loadScripts, callback){
8671             if(typeof html == "undefined"){
8672                 html = "";
8673             }
8674             if(loadScripts !== true){
8675                 this.dom.innerHTML = html;
8676                 if(typeof callback == "function"){
8677                     callback();
8678                 }
8679                 return this;
8680             }
8681             var id = Roo.id();
8682             var dom = this.dom;
8683
8684             html += '<span id="' + id + '"></span>';
8685
8686             E.onAvailable(id, function(){
8687                 var hd = document.getElementsByTagName("head")[0];
8688                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8689                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8690                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8691
8692                 var match;
8693                 while(match = re.exec(html)){
8694                     var attrs = match[1];
8695                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8696                     if(srcMatch && srcMatch[2]){
8697                        var s = document.createElement("script");
8698                        s.src = srcMatch[2];
8699                        var typeMatch = attrs.match(typeRe);
8700                        if(typeMatch && typeMatch[2]){
8701                            s.type = typeMatch[2];
8702                        }
8703                        hd.appendChild(s);
8704                     }else if(match[2] && match[2].length > 0){
8705                         if(window.execScript) {
8706                            window.execScript(match[2]);
8707                         } else {
8708                             /**
8709                              * eval:var:id
8710                              * eval:var:dom
8711                              * eval:var:html
8712                              * 
8713                              */
8714                            window.eval(match[2]);
8715                         }
8716                     }
8717                 }
8718                 var el = document.getElementById(id);
8719                 if(el){el.parentNode.removeChild(el);}
8720                 if(typeof callback == "function"){
8721                     callback();
8722                 }
8723             });
8724             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8725             return this;
8726         },
8727
8728         /**
8729          * Direct access to the UpdateManager update() method (takes the same parameters).
8730          * @param {String/Function} url The url for this request or a function to call to get the url
8731          * @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}
8732          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8733          * @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.
8734          * @return {Roo.Element} this
8735          */
8736         load : function(){
8737             var um = this.getUpdateManager();
8738             um.update.apply(um, arguments);
8739             return this;
8740         },
8741
8742         /**
8743         * Gets this element's UpdateManager
8744         * @return {Roo.UpdateManager} The UpdateManager
8745         */
8746         getUpdateManager : function(){
8747             if(!this.updateManager){
8748                 this.updateManager = new Roo.UpdateManager(this);
8749             }
8750             return this.updateManager;
8751         },
8752
8753         /**
8754          * Disables text selection for this element (normalized across browsers)
8755          * @return {Roo.Element} this
8756          */
8757         unselectable : function(){
8758             this.dom.unselectable = "on";
8759             this.swallowEvent("selectstart", true);
8760             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8761             this.addClass("x-unselectable");
8762             return this;
8763         },
8764
8765         /**
8766         * Calculates the x, y to center this element on the screen
8767         * @return {Array} The x, y values [x, y]
8768         */
8769         getCenterXY : function(){
8770             return this.getAlignToXY(document, 'c-c');
8771         },
8772
8773         /**
8774         * Centers the Element in either the viewport, or another Element.
8775         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8776         */
8777         center : function(centerIn){
8778             this.alignTo(centerIn || document, 'c-c');
8779             return this;
8780         },
8781
8782         /**
8783          * Tests various css rules/browsers to determine if this element uses a border box
8784          * @return {Boolean}
8785          */
8786         isBorderBox : function(){
8787             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8788         },
8789
8790         /**
8791          * Return a box {x, y, width, height} that can be used to set another elements
8792          * size/location to match this element.
8793          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8794          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8795          * @return {Object} box An object in the format {x, y, width, height}
8796          */
8797         getBox : function(contentBox, local){
8798             var xy;
8799             if(!local){
8800                 xy = this.getXY();
8801             }else{
8802                 var left = parseInt(this.getStyle("left"), 10) || 0;
8803                 var top = parseInt(this.getStyle("top"), 10) || 0;
8804                 xy = [left, top];
8805             }
8806             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8807             if(!contentBox){
8808                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8809             }else{
8810                 var l = this.getBorderWidth("l")+this.getPadding("l");
8811                 var r = this.getBorderWidth("r")+this.getPadding("r");
8812                 var t = this.getBorderWidth("t")+this.getPadding("t");
8813                 var b = this.getBorderWidth("b")+this.getPadding("b");
8814                 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)};
8815             }
8816             bx.right = bx.x + bx.width;
8817             bx.bottom = bx.y + bx.height;
8818             return bx;
8819         },
8820
8821         /**
8822          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8823          for more information about the sides.
8824          * @param {String} sides
8825          * @return {Number}
8826          */
8827         getFrameWidth : function(sides, onlyContentBox){
8828             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8829         },
8830
8831         /**
8832          * 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.
8833          * @param {Object} box The box to fill {x, y, width, height}
8834          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8836          * @return {Roo.Element} this
8837          */
8838         setBox : function(box, adjust, animate){
8839             var w = box.width, h = box.height;
8840             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8841                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8842                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8843             }
8844             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8845             return this;
8846         },
8847
8848         /**
8849          * Forces the browser to repaint this element
8850          * @return {Roo.Element} this
8851          */
8852          repaint : function(){
8853             var dom = this.dom;
8854             this.addClass("x-repaint");
8855             setTimeout(function(){
8856                 Roo.get(dom).removeClass("x-repaint");
8857             }, 1);
8858             return this;
8859         },
8860
8861         /**
8862          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8863          * then it returns the calculated width of the sides (see getPadding)
8864          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8865          * @return {Object/Number}
8866          */
8867         getMargins : function(side){
8868             if(!side){
8869                 return {
8870                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8871                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8872                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8873                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8874                 };
8875             }else{
8876                 return this.addStyles(side, El.margins);
8877              }
8878         },
8879
8880         // private
8881         addStyles : function(sides, styles){
8882             var val = 0, v, w;
8883             for(var i = 0, len = sides.length; i < len; i++){
8884                 v = this.getStyle(styles[sides.charAt(i)]);
8885                 if(v){
8886                      w = parseInt(v, 10);
8887                      if(w){ val += w; }
8888                 }
8889             }
8890             return val;
8891         },
8892
8893         /**
8894          * Creates a proxy element of this element
8895          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8896          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8897          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8898          * @return {Roo.Element} The new proxy element
8899          */
8900         createProxy : function(config, renderTo, matchBox){
8901             if(renderTo){
8902                 renderTo = Roo.getDom(renderTo);
8903             }else{
8904                 renderTo = document.body;
8905             }
8906             config = typeof config == "object" ?
8907                 config : {tag : "div", cls: config};
8908             var proxy = Roo.DomHelper.append(renderTo, config, true);
8909             if(matchBox){
8910                proxy.setBox(this.getBox());
8911             }
8912             return proxy;
8913         },
8914
8915         /**
8916          * Puts a mask over this element to disable user interaction. Requires core.css.
8917          * This method can only be applied to elements which accept child nodes.
8918          * @param {String} msg (optional) A message to display in the mask
8919          * @param {String} msgCls (optional) A css class to apply to the msg element
8920          * @return {Element} The mask  element
8921          */
8922         mask : function(msg, msgCls)
8923         {
8924             if(this.getStyle("position") == "static"){
8925                 this.setStyle("position", "relative");
8926             }
8927             if(!this._mask){
8928                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8929             }
8930             this.addClass("x-masked");
8931             this._mask.setDisplayed(true);
8932             
8933             // we wander
8934             var z = 0;
8935             var dom = this.dom
8936             while (dom && dom.style) {
8937                 if (!isNaN(parseInt(dom.style.zIndex))) {
8938                     z = Math.max(z, parseInt(dom.style.zIndex));
8939                 }
8940                 dom = dom.parentNode;
8941             }
8942             // if we are masking the body - then it hides everything..
8943             if (this.dom == document.body) {
8944                 z = 1000000;
8945                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8946                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8947             }
8948            
8949             if(typeof msg == 'string'){
8950                 if(!this._maskMsg){
8951                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8952                 }
8953                 var mm = this._maskMsg;
8954                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8955                 mm.dom.firstChild.innerHTML = msg;
8956                 mm.setDisplayed(true);
8957                 mm.center(this);
8958                 mm.setStyle('z-index', z + 102);
8959             }
8960             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8961                 this._mask.setHeight(this.getHeight());
8962             }
8963             this._mask.setStyle('z-index', z + 100);
8964             
8965             return this._mask;
8966         },
8967
8968         /**
8969          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8970          * it is cached for reuse.
8971          */
8972         unmask : function(removeEl){
8973             if(this._mask){
8974                 if(removeEl === true){
8975                     this._mask.remove();
8976                     delete this._mask;
8977                     if(this._maskMsg){
8978                         this._maskMsg.remove();
8979                         delete this._maskMsg;
8980                     }
8981                 }else{
8982                     this._mask.setDisplayed(false);
8983                     if(this._maskMsg){
8984                         this._maskMsg.setDisplayed(false);
8985                     }
8986                 }
8987             }
8988             this.removeClass("x-masked");
8989         },
8990
8991         /**
8992          * Returns true if this element is masked
8993          * @return {Boolean}
8994          */
8995         isMasked : function(){
8996             return this._mask && this._mask.isVisible();
8997         },
8998
8999         /**
9000          * Creates an iframe shim for this element to keep selects and other windowed objects from
9001          * showing through.
9002          * @return {Roo.Element} The new shim element
9003          */
9004         createShim : function(){
9005             var el = document.createElement('iframe');
9006             el.frameBorder = 'no';
9007             el.className = 'roo-shim';
9008             if(Roo.isIE && Roo.isSecure){
9009                 el.src = Roo.SSL_SECURE_URL;
9010             }
9011             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9012             shim.autoBoxAdjust = false;
9013             return shim;
9014         },
9015
9016         /**
9017          * Removes this element from the DOM and deletes it from the cache
9018          */
9019         remove : function(){
9020             if(this.dom.parentNode){
9021                 this.dom.parentNode.removeChild(this.dom);
9022             }
9023             delete El.cache[this.dom.id];
9024         },
9025
9026         /**
9027          * Sets up event handlers to add and remove a css class when the mouse is over this element
9028          * @param {String} className
9029          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9030          * mouseout events for children elements
9031          * @return {Roo.Element} this
9032          */
9033         addClassOnOver : function(className, preventFlicker){
9034             this.on("mouseover", function(){
9035                 Roo.fly(this, '_internal').addClass(className);
9036             }, this.dom);
9037             var removeFn = function(e){
9038                 if(preventFlicker !== true || !e.within(this, true)){
9039                     Roo.fly(this, '_internal').removeClass(className);
9040                 }
9041             };
9042             this.on("mouseout", removeFn, this.dom);
9043             return this;
9044         },
9045
9046         /**
9047          * Sets up event handlers to add and remove a css class when this element has the focus
9048          * @param {String} className
9049          * @return {Roo.Element} this
9050          */
9051         addClassOnFocus : function(className){
9052             this.on("focus", function(){
9053                 Roo.fly(this, '_internal').addClass(className);
9054             }, this.dom);
9055             this.on("blur", function(){
9056                 Roo.fly(this, '_internal').removeClass(className);
9057             }, this.dom);
9058             return this;
9059         },
9060         /**
9061          * 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)
9062          * @param {String} className
9063          * @return {Roo.Element} this
9064          */
9065         addClassOnClick : function(className){
9066             var dom = this.dom;
9067             this.on("mousedown", function(){
9068                 Roo.fly(dom, '_internal').addClass(className);
9069                 var d = Roo.get(document);
9070                 var fn = function(){
9071                     Roo.fly(dom, '_internal').removeClass(className);
9072                     d.removeListener("mouseup", fn);
9073                 };
9074                 d.on("mouseup", fn);
9075             });
9076             return this;
9077         },
9078
9079         /**
9080          * Stops the specified event from bubbling and optionally prevents the default action
9081          * @param {String} eventName
9082          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9083          * @return {Roo.Element} this
9084          */
9085         swallowEvent : function(eventName, preventDefault){
9086             var fn = function(e){
9087                 e.stopPropagation();
9088                 if(preventDefault){
9089                     e.preventDefault();
9090                 }
9091             };
9092             if(eventName instanceof Array){
9093                 for(var i = 0, len = eventName.length; i < len; i++){
9094                      this.on(eventName[i], fn);
9095                 }
9096                 return this;
9097             }
9098             this.on(eventName, fn);
9099             return this;
9100         },
9101
9102         /**
9103          * @private
9104          */
9105       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9106
9107         /**
9108          * Sizes this element to its parent element's dimensions performing
9109          * neccessary box adjustments.
9110          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9111          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9112          * @return {Roo.Element} this
9113          */
9114         fitToParent : function(monitorResize, targetParent) {
9115           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9116           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9117           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9118             return;
9119           }
9120           var p = Roo.get(targetParent || this.dom.parentNode);
9121           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9122           if (monitorResize === true) {
9123             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9124             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9125           }
9126           return this;
9127         },
9128
9129         /**
9130          * Gets the next sibling, skipping text nodes
9131          * @return {HTMLElement} The next sibling or null
9132          */
9133         getNextSibling : function(){
9134             var n = this.dom.nextSibling;
9135             while(n && n.nodeType != 1){
9136                 n = n.nextSibling;
9137             }
9138             return n;
9139         },
9140
9141         /**
9142          * Gets the previous sibling, skipping text nodes
9143          * @return {HTMLElement} The previous sibling or null
9144          */
9145         getPrevSibling : function(){
9146             var n = this.dom.previousSibling;
9147             while(n && n.nodeType != 1){
9148                 n = n.previousSibling;
9149             }
9150             return n;
9151         },
9152
9153
9154         /**
9155          * Appends the passed element(s) to this element
9156          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9157          * @return {Roo.Element} this
9158          */
9159         appendChild: function(el){
9160             el = Roo.get(el);
9161             el.appendTo(this);
9162             return this;
9163         },
9164
9165         /**
9166          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9167          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9168          * automatically generated with the specified attributes.
9169          * @param {HTMLElement} insertBefore (optional) a child element of this element
9170          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9171          * @return {Roo.Element} The new child element
9172          */
9173         createChild: function(config, insertBefore, returnDom){
9174             config = config || {tag:'div'};
9175             if(insertBefore){
9176                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9177             }
9178             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9179         },
9180
9181         /**
9182          * Appends this element to the passed element
9183          * @param {String/HTMLElement/Element} el The new parent element
9184          * @return {Roo.Element} this
9185          */
9186         appendTo: function(el){
9187             el = Roo.getDom(el);
9188             el.appendChild(this.dom);
9189             return this;
9190         },
9191
9192         /**
9193          * Inserts this element before the passed element in the DOM
9194          * @param {String/HTMLElement/Element} el The element to insert before
9195          * @return {Roo.Element} this
9196          */
9197         insertBefore: function(el){
9198             el = Roo.getDom(el);
9199             el.parentNode.insertBefore(this.dom, el);
9200             return this;
9201         },
9202
9203         /**
9204          * Inserts this element after the passed element in the DOM
9205          * @param {String/HTMLElement/Element} el The element to insert after
9206          * @return {Roo.Element} this
9207          */
9208         insertAfter: function(el){
9209             el = Roo.getDom(el);
9210             el.parentNode.insertBefore(this.dom, el.nextSibling);
9211             return this;
9212         },
9213
9214         /**
9215          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9216          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9217          * @return {Roo.Element} The new child
9218          */
9219         insertFirst: function(el, returnDom){
9220             el = el || {};
9221             if(typeof el == 'object' && !el.nodeType){ // dh config
9222                 return this.createChild(el, this.dom.firstChild, returnDom);
9223             }else{
9224                 el = Roo.getDom(el);
9225                 this.dom.insertBefore(el, this.dom.firstChild);
9226                 return !returnDom ? Roo.get(el) : el;
9227             }
9228         },
9229
9230         /**
9231          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9232          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9233          * @param {String} where (optional) 'before' or 'after' defaults to before
9234          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9235          * @return {Roo.Element} the inserted Element
9236          */
9237         insertSibling: function(el, where, returnDom){
9238             where = where ? where.toLowerCase() : 'before';
9239             el = el || {};
9240             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9241
9242             if(typeof el == 'object' && !el.nodeType){ // dh config
9243                 if(where == 'after' && !this.dom.nextSibling){
9244                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9245                 }else{
9246                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9247                 }
9248
9249             }else{
9250                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9251                             where == 'before' ? this.dom : this.dom.nextSibling);
9252                 if(!returnDom){
9253                     rt = Roo.get(rt);
9254                 }
9255             }
9256             return rt;
9257         },
9258
9259         /**
9260          * Creates and wraps this element with another element
9261          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9262          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9263          * @return {HTMLElement/Element} The newly created wrapper element
9264          */
9265         wrap: function(config, returnDom){
9266             if(!config){
9267                 config = {tag: "div"};
9268             }
9269             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9270             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9271             return newEl;
9272         },
9273
9274         /**
9275          * Replaces the passed element with this element
9276          * @param {String/HTMLElement/Element} el The element to replace
9277          * @return {Roo.Element} this
9278          */
9279         replace: function(el){
9280             el = Roo.get(el);
9281             this.insertBefore(el);
9282             el.remove();
9283             return this;
9284         },
9285
9286         /**
9287          * Inserts an html fragment into this element
9288          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9289          * @param {String} html The HTML fragment
9290          * @param {Boolean} returnEl True to return an Roo.Element
9291          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9292          */
9293         insertHtml : function(where, html, returnEl){
9294             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9295             return returnEl ? Roo.get(el) : el;
9296         },
9297
9298         /**
9299          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9300          * @param {Object} o The object with the attributes
9301          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9302          * @return {Roo.Element} this
9303          */
9304         set : function(o, useSet){
9305             var el = this.dom;
9306             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9307             for(var attr in o){
9308                 if(attr == "style" || typeof o[attr] == "function") continue;
9309                 if(attr=="cls"){
9310                     el.className = o["cls"];
9311                 }else{
9312                     if(useSet) el.setAttribute(attr, o[attr]);
9313                     else el[attr] = o[attr];
9314                 }
9315             }
9316             if(o.style){
9317                 Roo.DomHelper.applyStyles(el, o.style);
9318             }
9319             return this;
9320         },
9321
9322         /**
9323          * Convenience method for constructing a KeyMap
9324          * @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:
9325          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9326          * @param {Function} fn The function to call
9327          * @param {Object} scope (optional) The scope of the function
9328          * @return {Roo.KeyMap} The KeyMap created
9329          */
9330         addKeyListener : function(key, fn, scope){
9331             var config;
9332             if(typeof key != "object" || key instanceof Array){
9333                 config = {
9334                     key: key,
9335                     fn: fn,
9336                     scope: scope
9337                 };
9338             }else{
9339                 config = {
9340                     key : key.key,
9341                     shift : key.shift,
9342                     ctrl : key.ctrl,
9343                     alt : key.alt,
9344                     fn: fn,
9345                     scope: scope
9346                 };
9347             }
9348             return new Roo.KeyMap(this, config);
9349         },
9350
9351         /**
9352          * Creates a KeyMap for this element
9353          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9354          * @return {Roo.KeyMap} The KeyMap created
9355          */
9356         addKeyMap : function(config){
9357             return new Roo.KeyMap(this, config);
9358         },
9359
9360         /**
9361          * Returns true if this element is scrollable.
9362          * @return {Boolean}
9363          */
9364          isScrollable : function(){
9365             var dom = this.dom;
9366             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9367         },
9368
9369         /**
9370          * 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().
9371          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9372          * @param {Number} value The new scroll value
9373          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9374          * @return {Element} this
9375          */
9376
9377         scrollTo : function(side, value, animate){
9378             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9379             if(!animate || !A){
9380                 this.dom[prop] = value;
9381             }else{
9382                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9383                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9384             }
9385             return this;
9386         },
9387
9388         /**
9389          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9390          * within this element's scrollable range.
9391          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9392          * @param {Number} distance How far to scroll the element in pixels
9393          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9394          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9395          * was scrolled as far as it could go.
9396          */
9397          scroll : function(direction, distance, animate){
9398              if(!this.isScrollable()){
9399                  return;
9400              }
9401              var el = this.dom;
9402              var l = el.scrollLeft, t = el.scrollTop;
9403              var w = el.scrollWidth, h = el.scrollHeight;
9404              var cw = el.clientWidth, ch = el.clientHeight;
9405              direction = direction.toLowerCase();
9406              var scrolled = false;
9407              var a = this.preanim(arguments, 2);
9408              switch(direction){
9409                  case "l":
9410                  case "left":
9411                      if(w - l > cw){
9412                          var v = Math.min(l + distance, w-cw);
9413                          this.scrollTo("left", v, a);
9414                          scrolled = true;
9415                      }
9416                      break;
9417                 case "r":
9418                 case "right":
9419                      if(l > 0){
9420                          var v = Math.max(l - distance, 0);
9421                          this.scrollTo("left", v, a);
9422                          scrolled = true;
9423                      }
9424                      break;
9425                 case "t":
9426                 case "top":
9427                 case "up":
9428                      if(t > 0){
9429                          var v = Math.max(t - distance, 0);
9430                          this.scrollTo("top", v, a);
9431                          scrolled = true;
9432                      }
9433                      break;
9434                 case "b":
9435                 case "bottom":
9436                 case "down":
9437                      if(h - t > ch){
9438                          var v = Math.min(t + distance, h-ch);
9439                          this.scrollTo("top", v, a);
9440                          scrolled = true;
9441                      }
9442                      break;
9443              }
9444              return scrolled;
9445         },
9446
9447         /**
9448          * Translates the passed page coordinates into left/top css values for this element
9449          * @param {Number/Array} x The page x or an array containing [x, y]
9450          * @param {Number} y The page y
9451          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9452          */
9453         translatePoints : function(x, y){
9454             if(typeof x == 'object' || x instanceof Array){
9455                 y = x[1]; x = x[0];
9456             }
9457             var p = this.getStyle('position');
9458             var o = this.getXY();
9459
9460             var l = parseInt(this.getStyle('left'), 10);
9461             var t = parseInt(this.getStyle('top'), 10);
9462
9463             if(isNaN(l)){
9464                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9465             }
9466             if(isNaN(t)){
9467                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9468             }
9469
9470             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9471         },
9472
9473         /**
9474          * Returns the current scroll position of the element.
9475          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9476          */
9477         getScroll : function(){
9478             var d = this.dom, doc = document;
9479             if(d == doc || d == doc.body){
9480                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9481                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9482                 return {left: l, top: t};
9483             }else{
9484                 return {left: d.scrollLeft, top: d.scrollTop};
9485             }
9486         },
9487
9488         /**
9489          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9490          * are convert to standard 6 digit hex color.
9491          * @param {String} attr The css attribute
9492          * @param {String} defaultValue The default value to use when a valid color isn't found
9493          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9494          * YUI color anims.
9495          */
9496         getColor : function(attr, defaultValue, prefix){
9497             var v = this.getStyle(attr);
9498             if(!v || v == "transparent" || v == "inherit") {
9499                 return defaultValue;
9500             }
9501             var color = typeof prefix == "undefined" ? "#" : prefix;
9502             if(v.substr(0, 4) == "rgb("){
9503                 var rvs = v.slice(4, v.length -1).split(",");
9504                 for(var i = 0; i < 3; i++){
9505                     var h = parseInt(rvs[i]).toString(16);
9506                     if(h < 16){
9507                         h = "0" + h;
9508                     }
9509                     color += h;
9510                 }
9511             } else {
9512                 if(v.substr(0, 1) == "#"){
9513                     if(v.length == 4) {
9514                         for(var i = 1; i < 4; i++){
9515                             var c = v.charAt(i);
9516                             color +=  c + c;
9517                         }
9518                     }else if(v.length == 7){
9519                         color += v.substr(1);
9520                     }
9521                 }
9522             }
9523             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9524         },
9525
9526         /**
9527          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9528          * gradient background, rounded corners and a 4-way shadow.
9529          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9530          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9531          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9532          * @return {Roo.Element} this
9533          */
9534         boxWrap : function(cls){
9535             cls = cls || 'x-box';
9536             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9537             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9538             return el;
9539         },
9540
9541         /**
9542          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9543          * @param {String} namespace The namespace in which to look for the attribute
9544          * @param {String} name The attribute name
9545          * @return {String} The attribute value
9546          */
9547         getAttributeNS : Roo.isIE ? function(ns, name){
9548             var d = this.dom;
9549             var type = typeof d[ns+":"+name];
9550             if(type != 'undefined' && type != 'unknown'){
9551                 return d[ns+":"+name];
9552             }
9553             return d[name];
9554         } : function(ns, name){
9555             var d = this.dom;
9556             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9557         },
9558         
9559         
9560         /**
9561          * Sets or Returns the value the dom attribute value
9562          * @param {String} name The attribute name
9563          * @param {String} value (optional) The value to set the attribute to
9564          * @return {String} The attribute value
9565          */
9566         attr : function(name){
9567             if (arguments.length > 1) {
9568                 this.dom.setAttribute(name, arguments[1]);
9569                 return arguments[1];
9570             }
9571             if (!this.dom.hasAttribute(name)) {
9572                 return undefined;
9573             }
9574             return this.dom.getAttribute(name);
9575         }
9576         
9577         
9578         
9579     };
9580
9581     var ep = El.prototype;
9582
9583     /**
9584      * Appends an event handler (Shorthand for addListener)
9585      * @param {String}   eventName     The type of event to append
9586      * @param {Function} fn        The method the event invokes
9587      * @param {Object} scope       (optional) The scope (this object) of the fn
9588      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9589      * @method
9590      */
9591     ep.on = ep.addListener;
9592         // backwards compat
9593     ep.mon = ep.addListener;
9594
9595     /**
9596      * Removes an event handler from this element (shorthand for removeListener)
9597      * @param {String} eventName the type of event to remove
9598      * @param {Function} fn the method the event invokes
9599      * @return {Roo.Element} this
9600      * @method
9601      */
9602     ep.un = ep.removeListener;
9603
9604     /**
9605      * true to automatically adjust width and height settings for box-model issues (default to true)
9606      */
9607     ep.autoBoxAdjust = true;
9608
9609     // private
9610     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9611
9612     // private
9613     El.addUnits = function(v, defaultUnit){
9614         if(v === "" || v == "auto"){
9615             return v;
9616         }
9617         if(v === undefined){
9618             return '';
9619         }
9620         if(typeof v == "number" || !El.unitPattern.test(v)){
9621             return v + (defaultUnit || 'px');
9622         }
9623         return v;
9624     };
9625
9626     // special markup used throughout Roo when box wrapping elements
9627     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>';
9628     /**
9629      * Visibility mode constant - Use visibility to hide element
9630      * @static
9631      * @type Number
9632      */
9633     El.VISIBILITY = 1;
9634     /**
9635      * Visibility mode constant - Use display to hide element
9636      * @static
9637      * @type Number
9638      */
9639     El.DISPLAY = 2;
9640
9641     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9642     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9643     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9644
9645
9646
9647     /**
9648      * @private
9649      */
9650     El.cache = {};
9651
9652     var docEl;
9653
9654     /**
9655      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9656      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9657      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9658      * @return {Element} The Element object
9659      * @static
9660      */
9661     El.get = function(el){
9662         var ex, elm, id;
9663         if(!el){ return null; }
9664         if(typeof el == "string"){ // element id
9665             if(!(elm = document.getElementById(el))){
9666                 return null;
9667             }
9668             if(ex = El.cache[el]){
9669                 ex.dom = elm;
9670             }else{
9671                 ex = El.cache[el] = new El(elm);
9672             }
9673             return ex;
9674         }else if(el.tagName){ // dom element
9675             if(!(id = el.id)){
9676                 id = Roo.id(el);
9677             }
9678             if(ex = El.cache[id]){
9679                 ex.dom = el;
9680             }else{
9681                 ex = El.cache[id] = new El(el);
9682             }
9683             return ex;
9684         }else if(el instanceof El){
9685             if(el != docEl){
9686                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9687                                                               // catch case where it hasn't been appended
9688                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9689             }
9690             return el;
9691         }else if(el.isComposite){
9692             return el;
9693         }else if(el instanceof Array){
9694             return El.select(el);
9695         }else if(el == document){
9696             // create a bogus element object representing the document object
9697             if(!docEl){
9698                 var f = function(){};
9699                 f.prototype = El.prototype;
9700                 docEl = new f();
9701                 docEl.dom = document;
9702             }
9703             return docEl;
9704         }
9705         return null;
9706     };
9707
9708     // private
9709     El.uncache = function(el){
9710         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9711             if(a[i]){
9712                 delete El.cache[a[i].id || a[i]];
9713             }
9714         }
9715     };
9716
9717     // private
9718     // Garbage collection - uncache elements/purge listeners on orphaned elements
9719     // so we don't hold a reference and cause the browser to retain them
9720     El.garbageCollect = function(){
9721         if(!Roo.enableGarbageCollector){
9722             clearInterval(El.collectorThread);
9723             return;
9724         }
9725         for(var eid in El.cache){
9726             var el = El.cache[eid], d = el.dom;
9727             // -------------------------------------------------------
9728             // Determining what is garbage:
9729             // -------------------------------------------------------
9730             // !d
9731             // dom node is null, definitely garbage
9732             // -------------------------------------------------------
9733             // !d.parentNode
9734             // no parentNode == direct orphan, definitely garbage
9735             // -------------------------------------------------------
9736             // !d.offsetParent && !document.getElementById(eid)
9737             // display none elements have no offsetParent so we will
9738             // also try to look it up by it's id. However, check
9739             // offsetParent first so we don't do unneeded lookups.
9740             // This enables collection of elements that are not orphans
9741             // directly, but somewhere up the line they have an orphan
9742             // parent.
9743             // -------------------------------------------------------
9744             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9745                 delete El.cache[eid];
9746                 if(d && Roo.enableListenerCollection){
9747                     E.purgeElement(d);
9748                 }
9749             }
9750         }
9751     }
9752     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9753
9754
9755     // dom is optional
9756     El.Flyweight = function(dom){
9757         this.dom = dom;
9758     };
9759     El.Flyweight.prototype = El.prototype;
9760
9761     El._flyweights = {};
9762     /**
9763      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9764      * the dom node can be overwritten by other code.
9765      * @param {String/HTMLElement} el The dom node or id
9766      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9767      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9768      * @static
9769      * @return {Element} The shared Element object
9770      */
9771     El.fly = function(el, named){
9772         named = named || '_global';
9773         el = Roo.getDom(el);
9774         if(!el){
9775             return null;
9776         }
9777         if(!El._flyweights[named]){
9778             El._flyweights[named] = new El.Flyweight();
9779         }
9780         El._flyweights[named].dom = el;
9781         return El._flyweights[named];
9782     };
9783
9784     /**
9785      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9786      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9787      * Shorthand of {@link Roo.Element#get}
9788      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9789      * @return {Element} The Element object
9790      * @member Roo
9791      * @method get
9792      */
9793     Roo.get = El.get;
9794     /**
9795      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9796      * the dom node can be overwritten by other code.
9797      * Shorthand of {@link Roo.Element#fly}
9798      * @param {String/HTMLElement} el The dom node or id
9799      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9800      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9801      * @static
9802      * @return {Element} The shared Element object
9803      * @member Roo
9804      * @method fly
9805      */
9806     Roo.fly = El.fly;
9807
9808     // speedy lookup for elements never to box adjust
9809     var noBoxAdjust = Roo.isStrict ? {
9810         select:1
9811     } : {
9812         input:1, select:1, textarea:1
9813     };
9814     if(Roo.isIE || Roo.isGecko){
9815         noBoxAdjust['button'] = 1;
9816     }
9817
9818
9819     Roo.EventManager.on(window, 'unload', function(){
9820         delete El.cache;
9821         delete El._flyweights;
9822     });
9823 })();
9824
9825
9826
9827
9828 if(Roo.DomQuery){
9829     Roo.Element.selectorFunction = Roo.DomQuery.select;
9830 }
9831
9832 Roo.Element.select = function(selector, unique, root){
9833     var els;
9834     if(typeof selector == "string"){
9835         els = Roo.Element.selectorFunction(selector, root);
9836     }else if(selector.length !== undefined){
9837         els = selector;
9838     }else{
9839         throw "Invalid selector";
9840     }
9841     if(unique === true){
9842         return new Roo.CompositeElement(els);
9843     }else{
9844         return new Roo.CompositeElementLite(els);
9845     }
9846 };
9847 /**
9848  * Selects elements based on the passed CSS selector to enable working on them as 1.
9849  * @param {String/Array} selector The CSS selector or an array of elements
9850  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9851  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9852  * @return {CompositeElementLite/CompositeElement}
9853  * @member Roo
9854  * @method select
9855  */
9856 Roo.select = Roo.Element.select;
9857
9858
9859
9860
9861
9862
9863
9864
9865
9866
9867
9868
9869
9870
9871 /*
9872  * Based on:
9873  * Ext JS Library 1.1.1
9874  * Copyright(c) 2006-2007, Ext JS, LLC.
9875  *
9876  * Originally Released Under LGPL - original licence link has changed is not relivant.
9877  *
9878  * Fork - LGPL
9879  * <script type="text/javascript">
9880  */
9881
9882
9883
9884 //Notifies Element that fx methods are available
9885 Roo.enableFx = true;
9886
9887 /**
9888  * @class Roo.Fx
9889  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9890  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9891  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9892  * Element effects to work.</p><br/>
9893  *
9894  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9895  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9896  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9897  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9898  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9899  * expected results and should be done with care.</p><br/>
9900  *
9901  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9902  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9903 <pre>
9904 Value  Description
9905 -----  -----------------------------
9906 tl     The top left corner
9907 t      The center of the top edge
9908 tr     The top right corner
9909 l      The center of the left edge
9910 r      The center of the right edge
9911 bl     The bottom left corner
9912 b      The center of the bottom edge
9913 br     The bottom right corner
9914 </pre>
9915  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9916  * below are common options that can be passed to any Fx method.</b>
9917  * @cfg {Function} callback A function called when the effect is finished
9918  * @cfg {Object} scope The scope of the effect function
9919  * @cfg {String} easing A valid Easing value for the effect
9920  * @cfg {String} afterCls A css class to apply after the effect
9921  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9922  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9923  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9924  * effects that end with the element being visually hidden, ignored otherwise)
9925  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9926  * a function which returns such a specification that will be applied to the Element after the effect finishes
9927  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9928  * @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
9929  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9930  */
9931 Roo.Fx = {
9932         /**
9933          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9934          * origin for the slide effect.  This function automatically handles wrapping the element with
9935          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9936          * Usage:
9937          *<pre><code>
9938 // default: slide the element in from the top
9939 el.slideIn();
9940
9941 // custom: slide the element in from the right with a 2-second duration
9942 el.slideIn('r', { duration: 2 });
9943
9944 // common config options shown with default values
9945 el.slideIn('t', {
9946     easing: 'easeOut',
9947     duration: .5
9948 });
9949 </code></pre>
9950          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9951          * @param {Object} options (optional) Object literal with any of the Fx config options
9952          * @return {Roo.Element} The Element
9953          */
9954     slideIn : function(anchor, o){
9955         var el = this.getFxEl();
9956         o = o || {};
9957
9958         el.queueFx(o, function(){
9959
9960             anchor = anchor || "t";
9961
9962             // fix display to visibility
9963             this.fixDisplay();
9964
9965             // restore values after effect
9966             var r = this.getFxRestore();
9967             var b = this.getBox();
9968             // fixed size for slide
9969             this.setSize(b);
9970
9971             // wrap if needed
9972             var wrap = this.fxWrap(r.pos, o, "hidden");
9973
9974             var st = this.dom.style;
9975             st.visibility = "visible";
9976             st.position = "absolute";
9977
9978             // clear out temp styles after slide and unwrap
9979             var after = function(){
9980                 el.fxUnwrap(wrap, r.pos, o);
9981                 st.width = r.width;
9982                 st.height = r.height;
9983                 el.afterFx(o);
9984             };
9985             // time to calc the positions
9986             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9987
9988             switch(anchor.toLowerCase()){
9989                 case "t":
9990                     wrap.setSize(b.width, 0);
9991                     st.left = st.bottom = "0";
9992                     a = {height: bh};
9993                 break;
9994                 case "l":
9995                     wrap.setSize(0, b.height);
9996                     st.right = st.top = "0";
9997                     a = {width: bw};
9998                 break;
9999                 case "r":
10000                     wrap.setSize(0, b.height);
10001                     wrap.setX(b.right);
10002                     st.left = st.top = "0";
10003                     a = {width: bw, points: pt};
10004                 break;
10005                 case "b":
10006                     wrap.setSize(b.width, 0);
10007                     wrap.setY(b.bottom);
10008                     st.left = st.top = "0";
10009                     a = {height: bh, points: pt};
10010                 break;
10011                 case "tl":
10012                     wrap.setSize(0, 0);
10013                     st.right = st.bottom = "0";
10014                     a = {width: bw, height: bh};
10015                 break;
10016                 case "bl":
10017                     wrap.setSize(0, 0);
10018                     wrap.setY(b.y+b.height);
10019                     st.right = st.top = "0";
10020                     a = {width: bw, height: bh, points: pt};
10021                 break;
10022                 case "br":
10023                     wrap.setSize(0, 0);
10024                     wrap.setXY([b.right, b.bottom]);
10025                     st.left = st.top = "0";
10026                     a = {width: bw, height: bh, points: pt};
10027                 break;
10028                 case "tr":
10029                     wrap.setSize(0, 0);
10030                     wrap.setX(b.x+b.width);
10031                     st.left = st.bottom = "0";
10032                     a = {width: bw, height: bh, points: pt};
10033                 break;
10034             }
10035             this.dom.style.visibility = "visible";
10036             wrap.show();
10037
10038             arguments.callee.anim = wrap.fxanim(a,
10039                 o,
10040                 'motion',
10041                 .5,
10042                 'easeOut', after);
10043         });
10044         return this;
10045     },
10046     
10047         /**
10048          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10049          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10050          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10051          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10052          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10053          * Usage:
10054          *<pre><code>
10055 // default: slide the element out to the top
10056 el.slideOut();
10057
10058 // custom: slide the element out to the right with a 2-second duration
10059 el.slideOut('r', { duration: 2 });
10060
10061 // common config options shown with default values
10062 el.slideOut('t', {
10063     easing: 'easeOut',
10064     duration: .5,
10065     remove: false,
10066     useDisplay: false
10067 });
10068 </code></pre>
10069          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10070          * @param {Object} options (optional) Object literal with any of the Fx config options
10071          * @return {Roo.Element} The Element
10072          */
10073     slideOut : function(anchor, o){
10074         var el = this.getFxEl();
10075         o = o || {};
10076
10077         el.queueFx(o, function(){
10078
10079             anchor = anchor || "t";
10080
10081             // restore values after effect
10082             var r = this.getFxRestore();
10083             
10084             var b = this.getBox();
10085             // fixed size for slide
10086             this.setSize(b);
10087
10088             // wrap if needed
10089             var wrap = this.fxWrap(r.pos, o, "visible");
10090
10091             var st = this.dom.style;
10092             st.visibility = "visible";
10093             st.position = "absolute";
10094
10095             wrap.setSize(b);
10096
10097             var after = function(){
10098                 if(o.useDisplay){
10099                     el.setDisplayed(false);
10100                 }else{
10101                     el.hide();
10102                 }
10103
10104                 el.fxUnwrap(wrap, r.pos, o);
10105
10106                 st.width = r.width;
10107                 st.height = r.height;
10108
10109                 el.afterFx(o);
10110             };
10111
10112             var a, zero = {to: 0};
10113             switch(anchor.toLowerCase()){
10114                 case "t":
10115                     st.left = st.bottom = "0";
10116                     a = {height: zero};
10117                 break;
10118                 case "l":
10119                     st.right = st.top = "0";
10120                     a = {width: zero};
10121                 break;
10122                 case "r":
10123                     st.left = st.top = "0";
10124                     a = {width: zero, points: {to:[b.right, b.y]}};
10125                 break;
10126                 case "b":
10127                     st.left = st.top = "0";
10128                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10129                 break;
10130                 case "tl":
10131                     st.right = st.bottom = "0";
10132                     a = {width: zero, height: zero};
10133                 break;
10134                 case "bl":
10135                     st.right = st.top = "0";
10136                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10137                 break;
10138                 case "br":
10139                     st.left = st.top = "0";
10140                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10141                 break;
10142                 case "tr":
10143                     st.left = st.bottom = "0";
10144                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10145                 break;
10146             }
10147
10148             arguments.callee.anim = wrap.fxanim(a,
10149                 o,
10150                 'motion',
10151                 .5,
10152                 "easeOut", after);
10153         });
10154         return this;
10155     },
10156
10157         /**
10158          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10159          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10160          * The element must be removed from the DOM using the 'remove' config option if desired.
10161          * Usage:
10162          *<pre><code>
10163 // default
10164 el.puff();
10165
10166 // common config options shown with default values
10167 el.puff({
10168     easing: 'easeOut',
10169     duration: .5,
10170     remove: false,
10171     useDisplay: false
10172 });
10173 </code></pre>
10174          * @param {Object} options (optional) Object literal with any of the Fx config options
10175          * @return {Roo.Element} The Element
10176          */
10177     puff : function(o){
10178         var el = this.getFxEl();
10179         o = o || {};
10180
10181         el.queueFx(o, function(){
10182             this.clearOpacity();
10183             this.show();
10184
10185             // restore values after effect
10186             var r = this.getFxRestore();
10187             var st = this.dom.style;
10188
10189             var after = function(){
10190                 if(o.useDisplay){
10191                     el.setDisplayed(false);
10192                 }else{
10193                     el.hide();
10194                 }
10195
10196                 el.clearOpacity();
10197
10198                 el.setPositioning(r.pos);
10199                 st.width = r.width;
10200                 st.height = r.height;
10201                 st.fontSize = '';
10202                 el.afterFx(o);
10203             };
10204
10205             var width = this.getWidth();
10206             var height = this.getHeight();
10207
10208             arguments.callee.anim = this.fxanim({
10209                     width : {to: this.adjustWidth(width * 2)},
10210                     height : {to: this.adjustHeight(height * 2)},
10211                     points : {by: [-(width * .5), -(height * .5)]},
10212                     opacity : {to: 0},
10213                     fontSize: {to:200, unit: "%"}
10214                 },
10215                 o,
10216                 'motion',
10217                 .5,
10218                 "easeOut", after);
10219         });
10220         return this;
10221     },
10222
10223         /**
10224          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10225          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10226          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10227          * Usage:
10228          *<pre><code>
10229 // default
10230 el.switchOff();
10231
10232 // all config options shown with default values
10233 el.switchOff({
10234     easing: 'easeIn',
10235     duration: .3,
10236     remove: false,
10237     useDisplay: false
10238 });
10239 </code></pre>
10240          * @param {Object} options (optional) Object literal with any of the Fx config options
10241          * @return {Roo.Element} The Element
10242          */
10243     switchOff : function(o){
10244         var el = this.getFxEl();
10245         o = o || {};
10246
10247         el.queueFx(o, function(){
10248             this.clearOpacity();
10249             this.clip();
10250
10251             // restore values after effect
10252             var r = this.getFxRestore();
10253             var st = this.dom.style;
10254
10255             var after = function(){
10256                 if(o.useDisplay){
10257                     el.setDisplayed(false);
10258                 }else{
10259                     el.hide();
10260                 }
10261
10262                 el.clearOpacity();
10263                 el.setPositioning(r.pos);
10264                 st.width = r.width;
10265                 st.height = r.height;
10266
10267                 el.afterFx(o);
10268             };
10269
10270             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10271                 this.clearOpacity();
10272                 (function(){
10273                     this.fxanim({
10274                         height:{to:1},
10275                         points:{by:[0, this.getHeight() * .5]}
10276                     }, o, 'motion', 0.3, 'easeIn', after);
10277                 }).defer(100, this);
10278             });
10279         });
10280         return this;
10281     },
10282
10283     /**
10284      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10285      * changed using the "attr" config option) and then fading back to the original color. If no original
10286      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10287      * Usage:
10288 <pre><code>
10289 // default: highlight background to yellow
10290 el.highlight();
10291
10292 // custom: highlight foreground text to blue for 2 seconds
10293 el.highlight("0000ff", { attr: 'color', duration: 2 });
10294
10295 // common config options shown with default values
10296 el.highlight("ffff9c", {
10297     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10298     endColor: (current color) or "ffffff",
10299     easing: 'easeIn',
10300     duration: 1
10301 });
10302 </code></pre>
10303      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10304      * @param {Object} options (optional) Object literal with any of the Fx config options
10305      * @return {Roo.Element} The Element
10306      */ 
10307     highlight : function(color, o){
10308         var el = this.getFxEl();
10309         o = o || {};
10310
10311         el.queueFx(o, function(){
10312             color = color || "ffff9c";
10313             attr = o.attr || "backgroundColor";
10314
10315             this.clearOpacity();
10316             this.show();
10317
10318             var origColor = this.getColor(attr);
10319             var restoreColor = this.dom.style[attr];
10320             endColor = (o.endColor || origColor) || "ffffff";
10321
10322             var after = function(){
10323                 el.dom.style[attr] = restoreColor;
10324                 el.afterFx(o);
10325             };
10326
10327             var a = {};
10328             a[attr] = {from: color, to: endColor};
10329             arguments.callee.anim = this.fxanim(a,
10330                 o,
10331                 'color',
10332                 1,
10333                 'easeIn', after);
10334         });
10335         return this;
10336     },
10337
10338    /**
10339     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10340     * Usage:
10341 <pre><code>
10342 // default: a single light blue ripple
10343 el.frame();
10344
10345 // custom: 3 red ripples lasting 3 seconds total
10346 el.frame("ff0000", 3, { duration: 3 });
10347
10348 // common config options shown with default values
10349 el.frame("C3DAF9", 1, {
10350     duration: 1 //duration of entire animation (not each individual ripple)
10351     // Note: Easing is not configurable and will be ignored if included
10352 });
10353 </code></pre>
10354     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10355     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10356     * @param {Object} options (optional) Object literal with any of the Fx config options
10357     * @return {Roo.Element} The Element
10358     */
10359     frame : function(color, count, o){
10360         var el = this.getFxEl();
10361         o = o || {};
10362
10363         el.queueFx(o, function(){
10364             color = color || "#C3DAF9";
10365             if(color.length == 6){
10366                 color = "#" + color;
10367             }
10368             count = count || 1;
10369             duration = o.duration || 1;
10370             this.show();
10371
10372             var b = this.getBox();
10373             var animFn = function(){
10374                 var proxy = this.createProxy({
10375
10376                      style:{
10377                         visbility:"hidden",
10378                         position:"absolute",
10379                         "z-index":"35000", // yee haw
10380                         border:"0px solid " + color
10381                      }
10382                   });
10383                 var scale = Roo.isBorderBox ? 2 : 1;
10384                 proxy.animate({
10385                     top:{from:b.y, to:b.y - 20},
10386                     left:{from:b.x, to:b.x - 20},
10387                     borderWidth:{from:0, to:10},
10388                     opacity:{from:1, to:0},
10389                     height:{from:b.height, to:(b.height + (20*scale))},
10390                     width:{from:b.width, to:(b.width + (20*scale))}
10391                 }, duration, function(){
10392                     proxy.remove();
10393                 });
10394                 if(--count > 0){
10395                      animFn.defer((duration/2)*1000, this);
10396                 }else{
10397                     el.afterFx(o);
10398                 }
10399             };
10400             animFn.call(this);
10401         });
10402         return this;
10403     },
10404
10405    /**
10406     * Creates a pause before any subsequent queued effects begin.  If there are
10407     * no effects queued after the pause it will have no effect.
10408     * Usage:
10409 <pre><code>
10410 el.pause(1);
10411 </code></pre>
10412     * @param {Number} seconds The length of time to pause (in seconds)
10413     * @return {Roo.Element} The Element
10414     */
10415     pause : function(seconds){
10416         var el = this.getFxEl();
10417         var o = {};
10418
10419         el.queueFx(o, function(){
10420             setTimeout(function(){
10421                 el.afterFx(o);
10422             }, seconds * 1000);
10423         });
10424         return this;
10425     },
10426
10427    /**
10428     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10429     * using the "endOpacity" config option.
10430     * Usage:
10431 <pre><code>
10432 // default: fade in from opacity 0 to 100%
10433 el.fadeIn();
10434
10435 // custom: fade in from opacity 0 to 75% over 2 seconds
10436 el.fadeIn({ endOpacity: .75, duration: 2});
10437
10438 // common config options shown with default values
10439 el.fadeIn({
10440     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10441     easing: 'easeOut',
10442     duration: .5
10443 });
10444 </code></pre>
10445     * @param {Object} options (optional) Object literal with any of the Fx config options
10446     * @return {Roo.Element} The Element
10447     */
10448     fadeIn : function(o){
10449         var el = this.getFxEl();
10450         o = o || {};
10451         el.queueFx(o, function(){
10452             this.setOpacity(0);
10453             this.fixDisplay();
10454             this.dom.style.visibility = 'visible';
10455             var to = o.endOpacity || 1;
10456             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10457                 o, null, .5, "easeOut", function(){
10458                 if(to == 1){
10459                     this.clearOpacity();
10460                 }
10461                 el.afterFx(o);
10462             });
10463         });
10464         return this;
10465     },
10466
10467    /**
10468     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10469     * using the "endOpacity" config option.
10470     * Usage:
10471 <pre><code>
10472 // default: fade out from the element's current opacity to 0
10473 el.fadeOut();
10474
10475 // custom: fade out from the element's current opacity to 25% over 2 seconds
10476 el.fadeOut({ endOpacity: .25, duration: 2});
10477
10478 // common config options shown with default values
10479 el.fadeOut({
10480     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10481     easing: 'easeOut',
10482     duration: .5
10483     remove: false,
10484     useDisplay: false
10485 });
10486 </code></pre>
10487     * @param {Object} options (optional) Object literal with any of the Fx config options
10488     * @return {Roo.Element} The Element
10489     */
10490     fadeOut : function(o){
10491         var el = this.getFxEl();
10492         o = o || {};
10493         el.queueFx(o, function(){
10494             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10495                 o, null, .5, "easeOut", function(){
10496                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10497                      this.dom.style.display = "none";
10498                 }else{
10499                      this.dom.style.visibility = "hidden";
10500                 }
10501                 this.clearOpacity();
10502                 el.afterFx(o);
10503             });
10504         });
10505         return this;
10506     },
10507
10508    /**
10509     * Animates the transition of an element's dimensions from a starting height/width
10510     * to an ending height/width.
10511     * Usage:
10512 <pre><code>
10513 // change height and width to 100x100 pixels
10514 el.scale(100, 100);
10515
10516 // common config options shown with default values.  The height and width will default to
10517 // the element's existing values if passed as null.
10518 el.scale(
10519     [element's width],
10520     [element's height], {
10521     easing: 'easeOut',
10522     duration: .35
10523 });
10524 </code></pre>
10525     * @param {Number} width  The new width (pass undefined to keep the original width)
10526     * @param {Number} height  The new height (pass undefined to keep the original height)
10527     * @param {Object} options (optional) Object literal with any of the Fx config options
10528     * @return {Roo.Element} The Element
10529     */
10530     scale : function(w, h, o){
10531         this.shift(Roo.apply({}, o, {
10532             width: w,
10533             height: h
10534         }));
10535         return this;
10536     },
10537
10538    /**
10539     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10540     * Any of these properties not specified in the config object will not be changed.  This effect 
10541     * requires that at least one new dimension, position or opacity setting must be passed in on
10542     * the config object in order for the function to have any effect.
10543     * Usage:
10544 <pre><code>
10545 // slide the element horizontally to x position 200 while changing the height and opacity
10546 el.shift({ x: 200, height: 50, opacity: .8 });
10547
10548 // common config options shown with default values.
10549 el.shift({
10550     width: [element's width],
10551     height: [element's height],
10552     x: [element's x position],
10553     y: [element's y position],
10554     opacity: [element's opacity],
10555     easing: 'easeOut',
10556     duration: .35
10557 });
10558 </code></pre>
10559     * @param {Object} options  Object literal with any of the Fx config options
10560     * @return {Roo.Element} The Element
10561     */
10562     shift : function(o){
10563         var el = this.getFxEl();
10564         o = o || {};
10565         el.queueFx(o, function(){
10566             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10567             if(w !== undefined){
10568                 a.width = {to: this.adjustWidth(w)};
10569             }
10570             if(h !== undefined){
10571                 a.height = {to: this.adjustHeight(h)};
10572             }
10573             if(x !== undefined || y !== undefined){
10574                 a.points = {to: [
10575                     x !== undefined ? x : this.getX(),
10576                     y !== undefined ? y : this.getY()
10577                 ]};
10578             }
10579             if(op !== undefined){
10580                 a.opacity = {to: op};
10581             }
10582             if(o.xy !== undefined){
10583                 a.points = {to: o.xy};
10584             }
10585             arguments.callee.anim = this.fxanim(a,
10586                 o, 'motion', .35, "easeOut", function(){
10587                 el.afterFx(o);
10588             });
10589         });
10590         return this;
10591     },
10592
10593         /**
10594          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10595          * ending point of the effect.
10596          * Usage:
10597          *<pre><code>
10598 // default: slide the element downward while fading out
10599 el.ghost();
10600
10601 // custom: slide the element out to the right with a 2-second duration
10602 el.ghost('r', { duration: 2 });
10603
10604 // common config options shown with default values
10605 el.ghost('b', {
10606     easing: 'easeOut',
10607     duration: .5
10608     remove: false,
10609     useDisplay: false
10610 });
10611 </code></pre>
10612          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10613          * @param {Object} options (optional) Object literal with any of the Fx config options
10614          * @return {Roo.Element} The Element
10615          */
10616     ghost : function(anchor, o){
10617         var el = this.getFxEl();
10618         o = o || {};
10619
10620         el.queueFx(o, function(){
10621             anchor = anchor || "b";
10622
10623             // restore values after effect
10624             var r = this.getFxRestore();
10625             var w = this.getWidth(),
10626                 h = this.getHeight();
10627
10628             var st = this.dom.style;
10629
10630             var after = function(){
10631                 if(o.useDisplay){
10632                     el.setDisplayed(false);
10633                 }else{
10634                     el.hide();
10635                 }
10636
10637                 el.clearOpacity();
10638                 el.setPositioning(r.pos);
10639                 st.width = r.width;
10640                 st.height = r.height;
10641
10642                 el.afterFx(o);
10643             };
10644
10645             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10646             switch(anchor.toLowerCase()){
10647                 case "t":
10648                     pt.by = [0, -h];
10649                 break;
10650                 case "l":
10651                     pt.by = [-w, 0];
10652                 break;
10653                 case "r":
10654                     pt.by = [w, 0];
10655                 break;
10656                 case "b":
10657                     pt.by = [0, h];
10658                 break;
10659                 case "tl":
10660                     pt.by = [-w, -h];
10661                 break;
10662                 case "bl":
10663                     pt.by = [-w, h];
10664                 break;
10665                 case "br":
10666                     pt.by = [w, h];
10667                 break;
10668                 case "tr":
10669                     pt.by = [w, -h];
10670                 break;
10671             }
10672
10673             arguments.callee.anim = this.fxanim(a,
10674                 o,
10675                 'motion',
10676                 .5,
10677                 "easeOut", after);
10678         });
10679         return this;
10680     },
10681
10682         /**
10683          * Ensures that all effects queued after syncFx is called on the element are
10684          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10685          * @return {Roo.Element} The Element
10686          */
10687     syncFx : function(){
10688         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10689             block : false,
10690             concurrent : true,
10691             stopFx : false
10692         });
10693         return this;
10694     },
10695
10696         /**
10697          * Ensures that all effects queued after sequenceFx is called on the element are
10698          * run in sequence.  This is the opposite of {@link #syncFx}.
10699          * @return {Roo.Element} The Element
10700          */
10701     sequenceFx : function(){
10702         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10703             block : false,
10704             concurrent : false,
10705             stopFx : false
10706         });
10707         return this;
10708     },
10709
10710         /* @private */
10711     nextFx : function(){
10712         var ef = this.fxQueue[0];
10713         if(ef){
10714             ef.call(this);
10715         }
10716     },
10717
10718         /**
10719          * Returns true if the element has any effects actively running or queued, else returns false.
10720          * @return {Boolean} True if element has active effects, else false
10721          */
10722     hasActiveFx : function(){
10723         return this.fxQueue && this.fxQueue[0];
10724     },
10725
10726         /**
10727          * Stops any running effects and clears the element's internal effects queue if it contains
10728          * any additional effects that haven't started yet.
10729          * @return {Roo.Element} The Element
10730          */
10731     stopFx : function(){
10732         if(this.hasActiveFx()){
10733             var cur = this.fxQueue[0];
10734             if(cur && cur.anim && cur.anim.isAnimated()){
10735                 this.fxQueue = [cur]; // clear out others
10736                 cur.anim.stop(true);
10737             }
10738         }
10739         return this;
10740     },
10741
10742         /* @private */
10743     beforeFx : function(o){
10744         if(this.hasActiveFx() && !o.concurrent){
10745            if(o.stopFx){
10746                this.stopFx();
10747                return true;
10748            }
10749            return false;
10750         }
10751         return true;
10752     },
10753
10754         /**
10755          * Returns true if the element is currently blocking so that no other effect can be queued
10756          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10757          * used to ensure that an effect initiated by a user action runs to completion prior to the
10758          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10759          * @return {Boolean} True if blocking, else false
10760          */
10761     hasFxBlock : function(){
10762         var q = this.fxQueue;
10763         return q && q[0] && q[0].block;
10764     },
10765
10766         /* @private */
10767     queueFx : function(o, fn){
10768         if(!this.fxQueue){
10769             this.fxQueue = [];
10770         }
10771         if(!this.hasFxBlock()){
10772             Roo.applyIf(o, this.fxDefaults);
10773             if(!o.concurrent){
10774                 var run = this.beforeFx(o);
10775                 fn.block = o.block;
10776                 this.fxQueue.push(fn);
10777                 if(run){
10778                     this.nextFx();
10779                 }
10780             }else{
10781                 fn.call(this);
10782             }
10783         }
10784         return this;
10785     },
10786
10787         /* @private */
10788     fxWrap : function(pos, o, vis){
10789         var wrap;
10790         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10791             var wrapXY;
10792             if(o.fixPosition){
10793                 wrapXY = this.getXY();
10794             }
10795             var div = document.createElement("div");
10796             div.style.visibility = vis;
10797             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10798             wrap.setPositioning(pos);
10799             if(wrap.getStyle("position") == "static"){
10800                 wrap.position("relative");
10801             }
10802             this.clearPositioning('auto');
10803             wrap.clip();
10804             wrap.dom.appendChild(this.dom);
10805             if(wrapXY){
10806                 wrap.setXY(wrapXY);
10807             }
10808         }
10809         return wrap;
10810     },
10811
10812         /* @private */
10813     fxUnwrap : function(wrap, pos, o){
10814         this.clearPositioning();
10815         this.setPositioning(pos);
10816         if(!o.wrap){
10817             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10818             wrap.remove();
10819         }
10820     },
10821
10822         /* @private */
10823     getFxRestore : function(){
10824         var st = this.dom.style;
10825         return {pos: this.getPositioning(), width: st.width, height : st.height};
10826     },
10827
10828         /* @private */
10829     afterFx : function(o){
10830         if(o.afterStyle){
10831             this.applyStyles(o.afterStyle);
10832         }
10833         if(o.afterCls){
10834             this.addClass(o.afterCls);
10835         }
10836         if(o.remove === true){
10837             this.remove();
10838         }
10839         Roo.callback(o.callback, o.scope, [this]);
10840         if(!o.concurrent){
10841             this.fxQueue.shift();
10842             this.nextFx();
10843         }
10844     },
10845
10846         /* @private */
10847     getFxEl : function(){ // support for composite element fx
10848         return Roo.get(this.dom);
10849     },
10850
10851         /* @private */
10852     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10853         animType = animType || 'run';
10854         opt = opt || {};
10855         var anim = Roo.lib.Anim[animType](
10856             this.dom, args,
10857             (opt.duration || defaultDur) || .35,
10858             (opt.easing || defaultEase) || 'easeOut',
10859             function(){
10860                 Roo.callback(cb, this);
10861             },
10862             this
10863         );
10864         opt.anim = anim;
10865         return anim;
10866     }
10867 };
10868
10869 // backwords compat
10870 Roo.Fx.resize = Roo.Fx.scale;
10871
10872 //When included, Roo.Fx is automatically applied to Element so that all basic
10873 //effects are available directly via the Element API
10874 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10875  * Based on:
10876  * Ext JS Library 1.1.1
10877  * Copyright(c) 2006-2007, Ext JS, LLC.
10878  *
10879  * Originally Released Under LGPL - original licence link has changed is not relivant.
10880  *
10881  * Fork - LGPL
10882  * <script type="text/javascript">
10883  */
10884
10885
10886 /**
10887  * @class Roo.CompositeElement
10888  * Standard composite class. Creates a Roo.Element for every element in the collection.
10889  * <br><br>
10890  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10891  * actions will be performed on all the elements in this collection.</b>
10892  * <br><br>
10893  * All methods return <i>this</i> and can be chained.
10894  <pre><code>
10895  var els = Roo.select("#some-el div.some-class", true);
10896  // or select directly from an existing element
10897  var el = Roo.get('some-el');
10898  el.select('div.some-class', true);
10899
10900  els.setWidth(100); // all elements become 100 width
10901  els.hide(true); // all elements fade out and hide
10902  // or
10903  els.setWidth(100).hide(true);
10904  </code></pre>
10905  */
10906 Roo.CompositeElement = function(els){
10907     this.elements = [];
10908     this.addElements(els);
10909 };
10910 Roo.CompositeElement.prototype = {
10911     isComposite: true,
10912     addElements : function(els){
10913         if(!els) return this;
10914         if(typeof els == "string"){
10915             els = Roo.Element.selectorFunction(els);
10916         }
10917         var yels = this.elements;
10918         var index = yels.length-1;
10919         for(var i = 0, len = els.length; i < len; i++) {
10920                 yels[++index] = Roo.get(els[i]);
10921         }
10922         return this;
10923     },
10924
10925     /**
10926     * Clears this composite and adds the elements returned by the passed selector.
10927     * @param {String/Array} els A string CSS selector, an array of elements or an element
10928     * @return {CompositeElement} this
10929     */
10930     fill : function(els){
10931         this.elements = [];
10932         this.add(els);
10933         return this;
10934     },
10935
10936     /**
10937     * Filters this composite to only elements that match the passed selector.
10938     * @param {String} selector A string CSS selector
10939     * @return {CompositeElement} this
10940     */
10941     filter : function(selector){
10942         var els = [];
10943         this.each(function(el){
10944             if(el.is(selector)){
10945                 els[els.length] = el.dom;
10946             }
10947         });
10948         this.fill(els);
10949         return this;
10950     },
10951
10952     invoke : function(fn, args){
10953         var els = this.elements;
10954         for(var i = 0, len = els.length; i < len; i++) {
10955                 Roo.Element.prototype[fn].apply(els[i], args);
10956         }
10957         return this;
10958     },
10959     /**
10960     * Adds elements to this composite.
10961     * @param {String/Array} els A string CSS selector, an array of elements or an element
10962     * @return {CompositeElement} this
10963     */
10964     add : function(els){
10965         if(typeof els == "string"){
10966             this.addElements(Roo.Element.selectorFunction(els));
10967         }else if(els.length !== undefined){
10968             this.addElements(els);
10969         }else{
10970             this.addElements([els]);
10971         }
10972         return this;
10973     },
10974     /**
10975     * Calls the passed function passing (el, this, index) for each element in this composite.
10976     * @param {Function} fn The function to call
10977     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10978     * @return {CompositeElement} this
10979     */
10980     each : function(fn, scope){
10981         var els = this.elements;
10982         for(var i = 0, len = els.length; i < len; i++){
10983             if(fn.call(scope || els[i], els[i], this, i) === false) {
10984                 break;
10985             }
10986         }
10987         return this;
10988     },
10989
10990     /**
10991      * Returns the Element object at the specified index
10992      * @param {Number} index
10993      * @return {Roo.Element}
10994      */
10995     item : function(index){
10996         return this.elements[index] || null;
10997     },
10998
10999     /**
11000      * Returns the first Element
11001      * @return {Roo.Element}
11002      */
11003     first : function(){
11004         return this.item(0);
11005     },
11006
11007     /**
11008      * Returns the last Element
11009      * @return {Roo.Element}
11010      */
11011     last : function(){
11012         return this.item(this.elements.length-1);
11013     },
11014
11015     /**
11016      * Returns the number of elements in this composite
11017      * @return Number
11018      */
11019     getCount : function(){
11020         return this.elements.length;
11021     },
11022
11023     /**
11024      * Returns true if this composite contains the passed element
11025      * @return Boolean
11026      */
11027     contains : function(el){
11028         return this.indexOf(el) !== -1;
11029     },
11030
11031     /**
11032      * Returns true if this composite contains the passed element
11033      * @return Boolean
11034      */
11035     indexOf : function(el){
11036         return this.elements.indexOf(Roo.get(el));
11037     },
11038
11039
11040     /**
11041     * Removes the specified element(s).
11042     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11043     * or an array of any of those.
11044     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11045     * @return {CompositeElement} this
11046     */
11047     removeElement : function(el, removeDom){
11048         if(el instanceof Array){
11049             for(var i = 0, len = el.length; i < len; i++){
11050                 this.removeElement(el[i]);
11051             }
11052             return this;
11053         }
11054         var index = typeof el == 'number' ? el : this.indexOf(el);
11055         if(index !== -1){
11056             if(removeDom){
11057                 var d = this.elements[index];
11058                 if(d.dom){
11059                     d.remove();
11060                 }else{
11061                     d.parentNode.removeChild(d);
11062                 }
11063             }
11064             this.elements.splice(index, 1);
11065         }
11066         return this;
11067     },
11068
11069     /**
11070     * Replaces the specified element with the passed element.
11071     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11072     * to replace.
11073     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11074     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11075     * @return {CompositeElement} this
11076     */
11077     replaceElement : function(el, replacement, domReplace){
11078         var index = typeof el == 'number' ? el : this.indexOf(el);
11079         if(index !== -1){
11080             if(domReplace){
11081                 this.elements[index].replaceWith(replacement);
11082             }else{
11083                 this.elements.splice(index, 1, Roo.get(replacement))
11084             }
11085         }
11086         return this;
11087     },
11088
11089     /**
11090      * Removes all elements.
11091      */
11092     clear : function(){
11093         this.elements = [];
11094     }
11095 };
11096 (function(){
11097     Roo.CompositeElement.createCall = function(proto, fnName){
11098         if(!proto[fnName]){
11099             proto[fnName] = function(){
11100                 return this.invoke(fnName, arguments);
11101             };
11102         }
11103     };
11104     for(var fnName in Roo.Element.prototype){
11105         if(typeof Roo.Element.prototype[fnName] == "function"){
11106             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11107         }
11108     };
11109 })();
11110 /*
11111  * Based on:
11112  * Ext JS Library 1.1.1
11113  * Copyright(c) 2006-2007, Ext JS, LLC.
11114  *
11115  * Originally Released Under LGPL - original licence link has changed is not relivant.
11116  *
11117  * Fork - LGPL
11118  * <script type="text/javascript">
11119  */
11120
11121 /**
11122  * @class Roo.CompositeElementLite
11123  * @extends Roo.CompositeElement
11124  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11125  <pre><code>
11126  var els = Roo.select("#some-el div.some-class");
11127  // or select directly from an existing element
11128  var el = Roo.get('some-el');
11129  el.select('div.some-class');
11130
11131  els.setWidth(100); // all elements become 100 width
11132  els.hide(true); // all elements fade out and hide
11133  // or
11134  els.setWidth(100).hide(true);
11135  </code></pre><br><br>
11136  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11137  * actions will be performed on all the elements in this collection.</b>
11138  */
11139 Roo.CompositeElementLite = function(els){
11140     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11141     this.el = new Roo.Element.Flyweight();
11142 };
11143 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11144     addElements : function(els){
11145         if(els){
11146             if(els instanceof Array){
11147                 this.elements = this.elements.concat(els);
11148             }else{
11149                 var yels = this.elements;
11150                 var index = yels.length-1;
11151                 for(var i = 0, len = els.length; i < len; i++) {
11152                     yels[++index] = els[i];
11153                 }
11154             }
11155         }
11156         return this;
11157     },
11158     invoke : function(fn, args){
11159         var els = this.elements;
11160         var el = this.el;
11161         for(var i = 0, len = els.length; i < len; i++) {
11162             el.dom = els[i];
11163                 Roo.Element.prototype[fn].apply(el, args);
11164         }
11165         return this;
11166     },
11167     /**
11168      * Returns a flyweight Element of the dom element object at the specified index
11169      * @param {Number} index
11170      * @return {Roo.Element}
11171      */
11172     item : function(index){
11173         if(!this.elements[index]){
11174             return null;
11175         }
11176         this.el.dom = this.elements[index];
11177         return this.el;
11178     },
11179
11180     // fixes scope with flyweight
11181     addListener : function(eventName, handler, scope, opt){
11182         var els = this.elements;
11183         for(var i = 0, len = els.length; i < len; i++) {
11184             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11185         }
11186         return this;
11187     },
11188
11189     /**
11190     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11191     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11192     * a reference to the dom node, use el.dom.</b>
11193     * @param {Function} fn The function to call
11194     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11195     * @return {CompositeElement} this
11196     */
11197     each : function(fn, scope){
11198         var els = this.elements;
11199         var el = this.el;
11200         for(var i = 0, len = els.length; i < len; i++){
11201             el.dom = els[i];
11202                 if(fn.call(scope || el, el, this, i) === false){
11203                 break;
11204             }
11205         }
11206         return this;
11207     },
11208
11209     indexOf : function(el){
11210         return this.elements.indexOf(Roo.getDom(el));
11211     },
11212
11213     replaceElement : function(el, replacement, domReplace){
11214         var index = typeof el == 'number' ? el : this.indexOf(el);
11215         if(index !== -1){
11216             replacement = Roo.getDom(replacement);
11217             if(domReplace){
11218                 var d = this.elements[index];
11219                 d.parentNode.insertBefore(replacement, d);
11220                 d.parentNode.removeChild(d);
11221             }
11222             this.elements.splice(index, 1, replacement);
11223         }
11224         return this;
11225     }
11226 });
11227 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11228
11229 /*
11230  * Based on:
11231  * Ext JS Library 1.1.1
11232  * Copyright(c) 2006-2007, Ext JS, LLC.
11233  *
11234  * Originally Released Under LGPL - original licence link has changed is not relivant.
11235  *
11236  * Fork - LGPL
11237  * <script type="text/javascript">
11238  */
11239
11240  
11241
11242 /**
11243  * @class Roo.data.Connection
11244  * @extends Roo.util.Observable
11245  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11246  * either to a configured URL, or to a URL specified at request time.<br><br>
11247  * <p>
11248  * Requests made by this class are asynchronous, and will return immediately. No data from
11249  * the server will be available to the statement immediately following the {@link #request} call.
11250  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11251  * <p>
11252  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11253  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11254  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11255  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11256  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11257  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11258  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11259  * standard DOM methods.
11260  * @constructor
11261  * @param {Object} config a configuration object.
11262  */
11263 Roo.data.Connection = function(config){
11264     Roo.apply(this, config);
11265     this.addEvents({
11266         /**
11267          * @event beforerequest
11268          * Fires before a network request is made to retrieve a data object.
11269          * @param {Connection} conn This Connection object.
11270          * @param {Object} options The options config object passed to the {@link #request} method.
11271          */
11272         "beforerequest" : true,
11273         /**
11274          * @event requestcomplete
11275          * Fires if the request was successfully completed.
11276          * @param {Connection} conn This Connection object.
11277          * @param {Object} response The XHR object containing the response data.
11278          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11279          * @param {Object} options The options config object passed to the {@link #request} method.
11280          */
11281         "requestcomplete" : true,
11282         /**
11283          * @event requestexception
11284          * Fires if an error HTTP status was returned from the server.
11285          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
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         "requestexception" : true
11292     });
11293     Roo.data.Connection.superclass.constructor.call(this);
11294 };
11295
11296 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11297     /**
11298      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11299      */
11300     /**
11301      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11302      * extra parameters to each request made by this object. (defaults to undefined)
11303      */
11304     /**
11305      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11306      *  to each request made by this object. (defaults to undefined)
11307      */
11308     /**
11309      * @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)
11310      */
11311     /**
11312      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11313      */
11314     timeout : 30000,
11315     /**
11316      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11317      * @type Boolean
11318      */
11319     autoAbort:false,
11320
11321     /**
11322      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11323      * @type Boolean
11324      */
11325     disableCaching: true,
11326
11327     /**
11328      * Sends an HTTP request to a remote server.
11329      * @param {Object} options An object which may contain the following properties:<ul>
11330      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11331      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11332      * request, a url encoded string or a function to call to get either.</li>
11333      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11334      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11335      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11336      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11337      * <li>options {Object} The parameter to the request call.</li>
11338      * <li>success {Boolean} True if the request succeeded.</li>
11339      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11340      * </ul></li>
11341      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11342      * The callback is passed the following parameters:<ul>
11343      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11344      * <li>options {Object} The parameter to the request call.</li>
11345      * </ul></li>
11346      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11347      * The callback is passed the following parameters:<ul>
11348      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11349      * <li>options {Object} The parameter to the request call.</li>
11350      * </ul></li>
11351      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11352      * for the callback function. Defaults to the browser window.</li>
11353      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11354      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11355      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11356      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11357      * params for the post data. Any params will be appended to the URL.</li>
11358      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11359      * </ul>
11360      * @return {Number} transactionId
11361      */
11362     request : function(o){
11363         if(this.fireEvent("beforerequest", this, o) !== false){
11364             var p = o.params;
11365
11366             if(typeof p == "function"){
11367                 p = p.call(o.scope||window, o);
11368             }
11369             if(typeof p == "object"){
11370                 p = Roo.urlEncode(o.params);
11371             }
11372             if(this.extraParams){
11373                 var extras = Roo.urlEncode(this.extraParams);
11374                 p = p ? (p + '&' + extras) : extras;
11375             }
11376
11377             var url = o.url || this.url;
11378             if(typeof url == 'function'){
11379                 url = url.call(o.scope||window, o);
11380             }
11381
11382             if(o.form){
11383                 var form = Roo.getDom(o.form);
11384                 url = url || form.action;
11385
11386                 var enctype = form.getAttribute("enctype");
11387                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11388                     return this.doFormUpload(o, p, url);
11389                 }
11390                 var f = Roo.lib.Ajax.serializeForm(form);
11391                 p = p ? (p + '&' + f) : f;
11392             }
11393
11394             var hs = o.headers;
11395             if(this.defaultHeaders){
11396                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11397                 if(!o.headers){
11398                     o.headers = hs;
11399                 }
11400             }
11401
11402             var cb = {
11403                 success: this.handleResponse,
11404                 failure: this.handleFailure,
11405                 scope: this,
11406                 argument: {options: o},
11407                 timeout : o.timeout || this.timeout
11408             };
11409
11410             var method = o.method||this.method||(p ? "POST" : "GET");
11411
11412             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11413                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11414             }
11415
11416             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11417                 if(o.autoAbort){
11418                     this.abort();
11419                 }
11420             }else if(this.autoAbort !== false){
11421                 this.abort();
11422             }
11423
11424             if((method == 'GET' && p) || o.xmlData){
11425                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11426                 p = '';
11427             }
11428             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11429             return this.transId;
11430         }else{
11431             Roo.callback(o.callback, o.scope, [o, null, null]);
11432             return null;
11433         }
11434     },
11435
11436     /**
11437      * Determine whether this object has a request outstanding.
11438      * @param {Number} transactionId (Optional) defaults to the last transaction
11439      * @return {Boolean} True if there is an outstanding request.
11440      */
11441     isLoading : function(transId){
11442         if(transId){
11443             return Roo.lib.Ajax.isCallInProgress(transId);
11444         }else{
11445             return this.transId ? true : false;
11446         }
11447     },
11448
11449     /**
11450      * Aborts any outstanding request.
11451      * @param {Number} transactionId (Optional) defaults to the last transaction
11452      */
11453     abort : function(transId){
11454         if(transId || this.isLoading()){
11455             Roo.lib.Ajax.abort(transId || this.transId);
11456         }
11457     },
11458
11459     // private
11460     handleResponse : function(response){
11461         this.transId = false;
11462         var options = response.argument.options;
11463         response.argument = options ? options.argument : null;
11464         this.fireEvent("requestcomplete", this, response, options);
11465         Roo.callback(options.success, options.scope, [response, options]);
11466         Roo.callback(options.callback, options.scope, [options, true, response]);
11467     },
11468
11469     // private
11470     handleFailure : function(response, e){
11471         this.transId = false;
11472         var options = response.argument.options;
11473         response.argument = options ? options.argument : null;
11474         this.fireEvent("requestexception", this, response, options, e);
11475         Roo.callback(options.failure, options.scope, [response, options]);
11476         Roo.callback(options.callback, options.scope, [options, false, response]);
11477     },
11478
11479     // private
11480     doFormUpload : function(o, ps, url){
11481         var id = Roo.id();
11482         var frame = document.createElement('iframe');
11483         frame.id = id;
11484         frame.name = id;
11485         frame.className = 'x-hidden';
11486         if(Roo.isIE){
11487             frame.src = Roo.SSL_SECURE_URL;
11488         }
11489         document.body.appendChild(frame);
11490
11491         if(Roo.isIE){
11492            document.frames[id].name = id;
11493         }
11494
11495         var form = Roo.getDom(o.form);
11496         form.target = id;
11497         form.method = 'POST';
11498         form.enctype = form.encoding = 'multipart/form-data';
11499         if(url){
11500             form.action = url;
11501         }
11502
11503         var hiddens, hd;
11504         if(ps){ // add dynamic params
11505             hiddens = [];
11506             ps = Roo.urlDecode(ps, false);
11507             for(var k in ps){
11508                 if(ps.hasOwnProperty(k)){
11509                     hd = document.createElement('input');
11510                     hd.type = 'hidden';
11511                     hd.name = k;
11512                     hd.value = ps[k];
11513                     form.appendChild(hd);
11514                     hiddens.push(hd);
11515                 }
11516             }
11517         }
11518
11519         function cb(){
11520             var r = {  // bogus response object
11521                 responseText : '',
11522                 responseXML : null
11523             };
11524
11525             r.argument = o ? o.argument : null;
11526
11527             try { //
11528                 var doc;
11529                 if(Roo.isIE){
11530                     doc = frame.contentWindow.document;
11531                 }else {
11532                     doc = (frame.contentDocument || window.frames[id].document);
11533                 }
11534                 if(doc && doc.body){
11535                     r.responseText = doc.body.innerHTML;
11536                 }
11537                 if(doc && doc.XMLDocument){
11538                     r.responseXML = doc.XMLDocument;
11539                 }else {
11540                     r.responseXML = doc;
11541                 }
11542             }
11543             catch(e) {
11544                 // ignore
11545             }
11546
11547             Roo.EventManager.removeListener(frame, 'load', cb, this);
11548
11549             this.fireEvent("requestcomplete", this, r, o);
11550             Roo.callback(o.success, o.scope, [r, o]);
11551             Roo.callback(o.callback, o.scope, [o, true, r]);
11552
11553             setTimeout(function(){document.body.removeChild(frame);}, 100);
11554         }
11555
11556         Roo.EventManager.on(frame, 'load', cb, this);
11557         form.submit();
11558
11559         if(hiddens){ // remove dynamic params
11560             for(var i = 0, len = hiddens.length; i < len; i++){
11561                 form.removeChild(hiddens[i]);
11562             }
11563         }
11564     }
11565 });
11566 /*
11567  * Based on:
11568  * Ext JS Library 1.1.1
11569  * Copyright(c) 2006-2007, Ext JS, LLC.
11570  *
11571  * Originally Released Under LGPL - original licence link has changed is not relivant.
11572  *
11573  * Fork - LGPL
11574  * <script type="text/javascript">
11575  */
11576  
11577 /**
11578  * Global Ajax request class.
11579  * 
11580  * @class Roo.Ajax
11581  * @extends Roo.data.Connection
11582  * @static
11583  * 
11584  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11585  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11586  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11587  * @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)
11588  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11589  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11590  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11591  */
11592 Roo.Ajax = new Roo.data.Connection({
11593     // fix up the docs
11594     /**
11595      * @scope Roo.Ajax
11596      * @type {Boolear} 
11597      */
11598     autoAbort : false,
11599
11600     /**
11601      * Serialize the passed form into a url encoded string
11602      * @scope Roo.Ajax
11603      * @param {String/HTMLElement} form
11604      * @return {String}
11605      */
11606     serializeForm : function(form){
11607         return Roo.lib.Ajax.serializeForm(form);
11608     }
11609 });/*
11610  * Based on:
11611  * Ext JS Library 1.1.1
11612  * Copyright(c) 2006-2007, Ext JS, LLC.
11613  *
11614  * Originally Released Under LGPL - original licence link has changed is not relivant.
11615  *
11616  * Fork - LGPL
11617  * <script type="text/javascript">
11618  */
11619
11620  
11621 /**
11622  * @class Roo.UpdateManager
11623  * @extends Roo.util.Observable
11624  * Provides AJAX-style update for Element object.<br><br>
11625  * Usage:<br>
11626  * <pre><code>
11627  * // Get it from a Roo.Element object
11628  * var el = Roo.get("foo");
11629  * var mgr = el.getUpdateManager();
11630  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11631  * ...
11632  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11633  * <br>
11634  * // or directly (returns the same UpdateManager instance)
11635  * var mgr = new Roo.UpdateManager("myElementId");
11636  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11637  * mgr.on("update", myFcnNeedsToKnow);
11638  * <br>
11639    // short handed call directly from the element object
11640    Roo.get("foo").load({
11641         url: "bar.php",
11642         scripts:true,
11643         params: "for=bar",
11644         text: "Loading Foo..."
11645    });
11646  * </code></pre>
11647  * @constructor
11648  * Create new UpdateManager directly.
11649  * @param {String/HTMLElement/Roo.Element} el The element to update
11650  * @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).
11651  */
11652 Roo.UpdateManager = function(el, forceNew){
11653     el = Roo.get(el);
11654     if(!forceNew && el.updateManager){
11655         return el.updateManager;
11656     }
11657     /**
11658      * The Element object
11659      * @type Roo.Element
11660      */
11661     this.el = el;
11662     /**
11663      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11664      * @type String
11665      */
11666     this.defaultUrl = null;
11667
11668     this.addEvents({
11669         /**
11670          * @event beforeupdate
11671          * Fired before an update is made, return false from your handler and the update is cancelled.
11672          * @param {Roo.Element} el
11673          * @param {String/Object/Function} url
11674          * @param {String/Object} params
11675          */
11676         "beforeupdate": true,
11677         /**
11678          * @event update
11679          * Fired after successful update is made.
11680          * @param {Roo.Element} el
11681          * @param {Object} oResponseObject The response Object
11682          */
11683         "update": true,
11684         /**
11685          * @event failure
11686          * Fired on update failure.
11687          * @param {Roo.Element} el
11688          * @param {Object} oResponseObject The response Object
11689          */
11690         "failure": true
11691     });
11692     var d = Roo.UpdateManager.defaults;
11693     /**
11694      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11695      * @type String
11696      */
11697     this.sslBlankUrl = d.sslBlankUrl;
11698     /**
11699      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11700      * @type Boolean
11701      */
11702     this.disableCaching = d.disableCaching;
11703     /**
11704      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11705      * @type String
11706      */
11707     this.indicatorText = d.indicatorText;
11708     /**
11709      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11710      * @type String
11711      */
11712     this.showLoadIndicator = d.showLoadIndicator;
11713     /**
11714      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11715      * @type Number
11716      */
11717     this.timeout = d.timeout;
11718
11719     /**
11720      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11721      * @type Boolean
11722      */
11723     this.loadScripts = d.loadScripts;
11724
11725     /**
11726      * Transaction object of current executing transaction
11727      */
11728     this.transaction = null;
11729
11730     /**
11731      * @private
11732      */
11733     this.autoRefreshProcId = null;
11734     /**
11735      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11736      * @type Function
11737      */
11738     this.refreshDelegate = this.refresh.createDelegate(this);
11739     /**
11740      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11741      * @type Function
11742      */
11743     this.updateDelegate = this.update.createDelegate(this);
11744     /**
11745      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11746      * @type Function
11747      */
11748     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11749     /**
11750      * @private
11751      */
11752     this.successDelegate = this.processSuccess.createDelegate(this);
11753     /**
11754      * @private
11755      */
11756     this.failureDelegate = this.processFailure.createDelegate(this);
11757
11758     if(!this.renderer){
11759      /**
11760       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11761       */
11762     this.renderer = new Roo.UpdateManager.BasicRenderer();
11763     }
11764     
11765     Roo.UpdateManager.superclass.constructor.call(this);
11766 };
11767
11768 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11769     /**
11770      * Get the Element this UpdateManager is bound to
11771      * @return {Roo.Element} The element
11772      */
11773     getEl : function(){
11774         return this.el;
11775     },
11776     /**
11777      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11778      * @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:
11779 <pre><code>
11780 um.update({<br/>
11781     url: "your-url.php",<br/>
11782     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11783     callback: yourFunction,<br/>
11784     scope: yourObject, //(optional scope)  <br/>
11785     discardUrl: false, <br/>
11786     nocache: false,<br/>
11787     text: "Loading...",<br/>
11788     timeout: 30,<br/>
11789     scripts: false<br/>
11790 });
11791 </code></pre>
11792      * The only required property is url. The optional properties nocache, text and scripts
11793      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11794      * @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}
11795      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11796      * @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.
11797      */
11798     update : function(url, params, callback, discardUrl){
11799         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11800             var method = this.method,
11801                 cfg;
11802             if(typeof url == "object"){ // must be config object
11803                 cfg = url;
11804                 url = cfg.url;
11805                 params = params || cfg.params;
11806                 callback = callback || cfg.callback;
11807                 discardUrl = discardUrl || cfg.discardUrl;
11808                 if(callback && cfg.scope){
11809                     callback = callback.createDelegate(cfg.scope);
11810                 }
11811                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11812                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11813                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11814                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11815                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11816             }
11817             this.showLoading();
11818             if(!discardUrl){
11819                 this.defaultUrl = url;
11820             }
11821             if(typeof url == "function"){
11822                 url = url.call(this);
11823             }
11824
11825             method = method || (params ? "POST" : "GET");
11826             if(method == "GET"){
11827                 url = this.prepareUrl(url);
11828             }
11829
11830             var o = Roo.apply(cfg ||{}, {
11831                 url : url,
11832                 params: params,
11833                 success: this.successDelegate,
11834                 failure: this.failureDelegate,
11835                 callback: undefined,
11836                 timeout: (this.timeout*1000),
11837                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11838             });
11839             Roo.log("updated manager called with timeout of " + o.timeout);
11840             this.transaction = Roo.Ajax.request(o);
11841         }
11842     },
11843
11844     /**
11845      * 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.
11846      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11847      * @param {String/HTMLElement} form The form Id or form element
11848      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11849      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11850      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11851      */
11852     formUpdate : function(form, url, reset, callback){
11853         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11854             if(typeof url == "function"){
11855                 url = url.call(this);
11856             }
11857             form = Roo.getDom(form);
11858             this.transaction = Roo.Ajax.request({
11859                 form: form,
11860                 url:url,
11861                 success: this.successDelegate,
11862                 failure: this.failureDelegate,
11863                 timeout: (this.timeout*1000),
11864                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11865             });
11866             this.showLoading.defer(1, this);
11867         }
11868     },
11869
11870     /**
11871      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11872      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11873      */
11874     refresh : function(callback){
11875         if(this.defaultUrl == null){
11876             return;
11877         }
11878         this.update(this.defaultUrl, null, callback, true);
11879     },
11880
11881     /**
11882      * Set this element to auto refresh.
11883      * @param {Number} interval How often to update (in seconds).
11884      * @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)
11885      * @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}
11886      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11887      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11888      */
11889     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11890         if(refreshNow){
11891             this.update(url || this.defaultUrl, params, callback, true);
11892         }
11893         if(this.autoRefreshProcId){
11894             clearInterval(this.autoRefreshProcId);
11895         }
11896         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11897     },
11898
11899     /**
11900      * Stop auto refresh on this element.
11901      */
11902      stopAutoRefresh : function(){
11903         if(this.autoRefreshProcId){
11904             clearInterval(this.autoRefreshProcId);
11905             delete this.autoRefreshProcId;
11906         }
11907     },
11908
11909     isAutoRefreshing : function(){
11910        return this.autoRefreshProcId ? true : false;
11911     },
11912     /**
11913      * Called to update the element to "Loading" state. Override to perform custom action.
11914      */
11915     showLoading : function(){
11916         if(this.showLoadIndicator){
11917             this.el.update(this.indicatorText);
11918         }
11919     },
11920
11921     /**
11922      * Adds unique parameter to query string if disableCaching = true
11923      * @private
11924      */
11925     prepareUrl : function(url){
11926         if(this.disableCaching){
11927             var append = "_dc=" + (new Date().getTime());
11928             if(url.indexOf("?") !== -1){
11929                 url += "&" + append;
11930             }else{
11931                 url += "?" + append;
11932             }
11933         }
11934         return url;
11935     },
11936
11937     /**
11938      * @private
11939      */
11940     processSuccess : function(response){
11941         this.transaction = null;
11942         if(response.argument.form && response.argument.reset){
11943             try{ // put in try/catch since some older FF releases had problems with this
11944                 response.argument.form.reset();
11945             }catch(e){}
11946         }
11947         if(this.loadScripts){
11948             this.renderer.render(this.el, response, this,
11949                 this.updateComplete.createDelegate(this, [response]));
11950         }else{
11951             this.renderer.render(this.el, response, this);
11952             this.updateComplete(response);
11953         }
11954     },
11955
11956     updateComplete : function(response){
11957         this.fireEvent("update", this.el, response);
11958         if(typeof response.argument.callback == "function"){
11959             response.argument.callback(this.el, true, response);
11960         }
11961     },
11962
11963     /**
11964      * @private
11965      */
11966     processFailure : function(response){
11967         this.transaction = null;
11968         this.fireEvent("failure", this.el, response);
11969         if(typeof response.argument.callback == "function"){
11970             response.argument.callback(this.el, false, response);
11971         }
11972     },
11973
11974     /**
11975      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11976      * @param {Object} renderer The object implementing the render() method
11977      */
11978     setRenderer : function(renderer){
11979         this.renderer = renderer;
11980     },
11981
11982     getRenderer : function(){
11983        return this.renderer;
11984     },
11985
11986     /**
11987      * Set the defaultUrl used for updates
11988      * @param {String/Function} defaultUrl The url or a function to call to get the url
11989      */
11990     setDefaultUrl : function(defaultUrl){
11991         this.defaultUrl = defaultUrl;
11992     },
11993
11994     /**
11995      * Aborts the executing transaction
11996      */
11997     abort : function(){
11998         if(this.transaction){
11999             Roo.Ajax.abort(this.transaction);
12000         }
12001     },
12002
12003     /**
12004      * Returns true if an update is in progress
12005      * @return {Boolean}
12006      */
12007     isUpdating : function(){
12008         if(this.transaction){
12009             return Roo.Ajax.isLoading(this.transaction);
12010         }
12011         return false;
12012     }
12013 });
12014
12015 /**
12016  * @class Roo.UpdateManager.defaults
12017  * @static (not really - but it helps the doc tool)
12018  * The defaults collection enables customizing the default properties of UpdateManager
12019  */
12020    Roo.UpdateManager.defaults = {
12021        /**
12022          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12023          * @type Number
12024          */
12025          timeout : 30,
12026
12027          /**
12028          * True to process scripts by default (Defaults to false).
12029          * @type Boolean
12030          */
12031         loadScripts : false,
12032
12033         /**
12034         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12035         * @type String
12036         */
12037         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12038         /**
12039          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12040          * @type Boolean
12041          */
12042         disableCaching : false,
12043         /**
12044          * Whether to show indicatorText when loading (Defaults to true).
12045          * @type Boolean
12046          */
12047         showLoadIndicator : true,
12048         /**
12049          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12050          * @type String
12051          */
12052         indicatorText : '<div class="loading-indicator">Loading...</div>'
12053    };
12054
12055 /**
12056  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12057  *Usage:
12058  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12059  * @param {String/HTMLElement/Roo.Element} el The element to update
12060  * @param {String} url The url
12061  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12062  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12063  * @static
12064  * @deprecated
12065  * @member Roo.UpdateManager
12066  */
12067 Roo.UpdateManager.updateElement = function(el, url, params, options){
12068     var um = Roo.get(el, true).getUpdateManager();
12069     Roo.apply(um, options);
12070     um.update(url, params, options ? options.callback : null);
12071 };
12072 // alias for backwards compat
12073 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12074 /**
12075  * @class Roo.UpdateManager.BasicRenderer
12076  * Default Content renderer. Updates the elements innerHTML with the responseText.
12077  */
12078 Roo.UpdateManager.BasicRenderer = function(){};
12079
12080 Roo.UpdateManager.BasicRenderer.prototype = {
12081     /**
12082      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12083      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12084      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12085      * @param {Roo.Element} el The element being rendered
12086      * @param {Object} response The YUI Connect response object
12087      * @param {UpdateManager} updateManager The calling update manager
12088      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12089      */
12090      render : function(el, response, updateManager, callback){
12091         el.update(response.responseText, updateManager.loadScripts, callback);
12092     }
12093 };
12094 /*
12095  * Based on:
12096  * Roo JS
12097  * (c)) Alan Knowles
12098  * Licence : LGPL
12099  */
12100
12101
12102 /**
12103  * @class Roo.DomTemplate
12104  * @extends Roo.Template
12105  * An effort at a dom based template engine..
12106  *
12107  * Similar to XTemplate, except it uses dom parsing to create the template..
12108  *
12109  * Supported features:
12110  *
12111  *  Tags:
12112
12113 <pre><code>
12114       {a_variable} - output encoded.
12115       {a_variable.format:("Y-m-d")} - call a method on the variable
12116       {a_variable:raw} - unencoded output
12117       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12118       {a_variable:this.method_on_template(...)} - call a method on the template object.
12119  
12120 </code></pre>
12121  *  The tpl tag:
12122 <pre><code>
12123         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12124         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12125         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12126         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12127   
12128 </code></pre>
12129  *      
12130  */
12131 Roo.DomTemplate = function()
12132 {
12133      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12134      if (this.html) {
12135         this.compile();
12136      }
12137 };
12138
12139
12140 Roo.extend(Roo.DomTemplate, Roo.Template, {
12141     /**
12142      * id counter for sub templates.
12143      */
12144     id : 0,
12145     /**
12146      * flag to indicate if dom parser is inside a pre,
12147      * it will strip whitespace if not.
12148      */
12149     inPre : false,
12150     
12151     /**
12152      * The various sub templates
12153      */
12154     tpls : false,
12155     
12156     
12157     
12158     /**
12159      *
12160      * basic tag replacing syntax
12161      * WORD:WORD()
12162      *
12163      * // you can fake an object call by doing this
12164      *  x.t:(test,tesT) 
12165      * 
12166      */
12167     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12168     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12169     
12170     iterChild : function (node, method) {
12171         
12172         var oldPre = this.inPre;
12173         if (node.tagName == 'PRE') {
12174             this.inPre = true;
12175         }
12176         for( var i = 0; i < node.childNodes.length; i++) {
12177             method.call(this, node.childNodes[i]);
12178         }
12179         this.inPre = oldPre;
12180     },
12181     
12182     
12183     
12184     /**
12185      * compile the template
12186      *
12187      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12188      *
12189      */
12190     compile: function()
12191     {
12192         var s = this.html;
12193         
12194         // covert the html into DOM...
12195         var doc = false;
12196         var div =false;
12197         try {
12198             doc = document.implementation.createHTMLDocument("");
12199             doc.documentElement.innerHTML =   this.html  ;
12200             div = doc.documentElement;
12201         } catch (e) {
12202             // old IE... - nasty -- it causes all sorts of issues.. with
12203             // images getting pulled from server..
12204             div = document.createElement('div');
12205             div.innerHTML = this.html;
12206         }
12207         //doc.documentElement.innerHTML = htmlBody
12208          
12209         
12210         
12211         this.tpls = [];
12212         var _t = this;
12213         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12214         
12215         var tpls = this.tpls;
12216         
12217         // create a top level template from the snippet..
12218         
12219         //Roo.log(div.innerHTML);
12220         
12221         var tpl = {
12222             uid : 'master',
12223             id : this.id++,
12224             attr : false,
12225             value : false,
12226             body : div.innerHTML,
12227             
12228             forCall : false,
12229             execCall : false,
12230             dom : div,
12231             isTop : true
12232             
12233         };
12234         tpls.unshift(tpl);
12235         
12236         
12237         // compile them...
12238         this.tpls = [];
12239         Roo.each(tpls, function(tp){
12240             this.compileTpl(tp);
12241             this.tpls[tp.id] = tp;
12242         }, this);
12243         
12244         this.master = tpls[0];
12245         return this;
12246         
12247         
12248     },
12249     
12250     compileNode : function(node, istop) {
12251         // test for
12252         //Roo.log(node);
12253         
12254         
12255         // skip anything not a tag..
12256         if (node.nodeType != 1) {
12257             if (node.nodeType == 3 && !this.inPre) {
12258                 // reduce white space..
12259                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12260                 
12261             }
12262             return;
12263         }
12264         
12265         var tpl = {
12266             uid : false,
12267             id : false,
12268             attr : false,
12269             value : false,
12270             body : '',
12271             
12272             forCall : false,
12273             execCall : false,
12274             dom : false,
12275             isTop : istop
12276             
12277             
12278         };
12279         
12280         
12281         switch(true) {
12282             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12283             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12284             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12285             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12286             // no default..
12287         }
12288         
12289         
12290         if (!tpl.attr) {
12291             // just itterate children..
12292             this.iterChild(node,this.compileNode);
12293             return;
12294         }
12295         tpl.uid = this.id++;
12296         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12297         node.removeAttribute('roo-'+ tpl.attr);
12298         if (tpl.attr != 'name') {
12299             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12300             node.parentNode.replaceChild(placeholder,  node);
12301         } else {
12302             
12303             var placeholder =  document.createElement('span');
12304             placeholder.className = 'roo-tpl-' + tpl.value;
12305             node.parentNode.replaceChild(placeholder,  node);
12306         }
12307         
12308         // parent now sees '{domtplXXXX}
12309         this.iterChild(node,this.compileNode);
12310         
12311         // we should now have node body...
12312         var div = document.createElement('div');
12313         div.appendChild(node);
12314         tpl.dom = node;
12315         // this has the unfortunate side effect of converting tagged attributes
12316         // eg. href="{...}" into %7C...%7D
12317         // this has been fixed by searching for those combo's although it's a bit hacky..
12318         
12319         
12320         tpl.body = div.innerHTML;
12321         
12322         
12323          
12324         tpl.id = tpl.uid;
12325         switch(tpl.attr) {
12326             case 'for' :
12327                 switch (tpl.value) {
12328                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12329                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12330                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12331                 }
12332                 break;
12333             
12334             case 'exec':
12335                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12336                 break;
12337             
12338             case 'if':     
12339                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12340                 break;
12341             
12342             case 'name':
12343                 tpl.id  = tpl.value; // replace non characters???
12344                 break;
12345             
12346         }
12347         
12348         
12349         this.tpls.push(tpl);
12350         
12351         
12352         
12353     },
12354     
12355     
12356     
12357     
12358     /**
12359      * Compile a segment of the template into a 'sub-template'
12360      *
12361      * 
12362      * 
12363      *
12364      */
12365     compileTpl : function(tpl)
12366     {
12367         var fm = Roo.util.Format;
12368         var useF = this.disableFormats !== true;
12369         
12370         var sep = Roo.isGecko ? "+\n" : ",\n";
12371         
12372         var undef = function(str) {
12373             Roo.debug && Roo.log("Property not found :"  + str);
12374             return '';
12375         };
12376           
12377         //Roo.log(tpl.body);
12378         
12379         
12380         
12381         var fn = function(m, lbrace, name, format, args)
12382         {
12383             //Roo.log("ARGS");
12384             //Roo.log(arguments);
12385             args = args ? args.replace(/\\'/g,"'") : args;
12386             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12387             if (typeof(format) == 'undefined') {
12388                 format =  'htmlEncode'; 
12389             }
12390             if (format == 'raw' ) {
12391                 format = false;
12392             }
12393             
12394             if(name.substr(0, 6) == 'domtpl'){
12395                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12396             }
12397             
12398             // build an array of options to determine if value is undefined..
12399             
12400             // basically get 'xxxx.yyyy' then do
12401             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12402             //    (function () { Roo.log("Property not found"); return ''; })() :
12403             //    ......
12404             
12405             var udef_ar = [];
12406             var lookfor = '';
12407             Roo.each(name.split('.'), function(st) {
12408                 lookfor += (lookfor.length ? '.': '') + st;
12409                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12410             });
12411             
12412             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12413             
12414             
12415             if(format && useF){
12416                 
12417                 args = args ? ',' + args : "";
12418                  
12419                 if(format.substr(0, 5) != "this."){
12420                     format = "fm." + format + '(';
12421                 }else{
12422                     format = 'this.call("'+ format.substr(5) + '", ';
12423                     args = ", values";
12424                 }
12425                 
12426                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12427             }
12428              
12429             if (args && args.length) {
12430                 // called with xxyx.yuu:(test,test)
12431                 // change to ()
12432                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12433             }
12434             // raw.. - :raw modifier..
12435             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12436             
12437         };
12438         var body;
12439         // branched to use + in gecko and [].join() in others
12440         if(Roo.isGecko){
12441             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12442                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12443                     "';};};";
12444         }else{
12445             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12446             body.push(tpl.body.replace(/(\r\n|\n)/g,
12447                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12448             body.push("'].join('');};};");
12449             body = body.join('');
12450         }
12451         
12452         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12453        
12454         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12455         eval(body);
12456         
12457         return this;
12458     },
12459      
12460     /**
12461      * same as applyTemplate, except it's done to one of the subTemplates
12462      * when using named templates, you can do:
12463      *
12464      * var str = pl.applySubTemplate('your-name', values);
12465      *
12466      * 
12467      * @param {Number} id of the template
12468      * @param {Object} values to apply to template
12469      * @param {Object} parent (normaly the instance of this object)
12470      */
12471     applySubTemplate : function(id, values, parent)
12472     {
12473         
12474         
12475         var t = this.tpls[id];
12476         
12477         
12478         try { 
12479             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12480                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12481                 return '';
12482             }
12483         } catch(e) {
12484             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12485             Roo.log(values);
12486           
12487             return '';
12488         }
12489         try { 
12490             
12491             if(t.execCall && t.execCall.call(this, values, parent)){
12492                 return '';
12493             }
12494         } catch(e) {
12495             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12496             Roo.log(values);
12497             return '';
12498         }
12499         
12500         try {
12501             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12502             parent = t.target ? values : parent;
12503             if(t.forCall && vs instanceof Array){
12504                 var buf = [];
12505                 for(var i = 0, len = vs.length; i < len; i++){
12506                     try {
12507                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12508                     } catch (e) {
12509                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12510                         Roo.log(e.body);
12511                         //Roo.log(t.compiled);
12512                         Roo.log(vs[i]);
12513                     }   
12514                 }
12515                 return buf.join('');
12516             }
12517         } catch (e) {
12518             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12519             Roo.log(values);
12520             return '';
12521         }
12522         try {
12523             return t.compiled.call(this, vs, parent);
12524         } catch (e) {
12525             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12526             Roo.log(e.body);
12527             //Roo.log(t.compiled);
12528             Roo.log(values);
12529             return '';
12530         }
12531     },
12532
12533    
12534
12535     applyTemplate : function(values){
12536         return this.master.compiled.call(this, values, {});
12537         //var s = this.subs;
12538     },
12539
12540     apply : function(){
12541         return this.applyTemplate.apply(this, arguments);
12542     }
12543
12544  });
12545
12546 Roo.DomTemplate.from = function(el){
12547     el = Roo.getDom(el);
12548     return new Roo.Domtemplate(el.value || el.innerHTML);
12549 };/*
12550  * Based on:
12551  * Ext JS Library 1.1.1
12552  * Copyright(c) 2006-2007, Ext JS, LLC.
12553  *
12554  * Originally Released Under LGPL - original licence link has changed is not relivant.
12555  *
12556  * Fork - LGPL
12557  * <script type="text/javascript">
12558  */
12559
12560 /**
12561  * @class Roo.util.DelayedTask
12562  * Provides a convenient method of performing setTimeout where a new
12563  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12564  * You can use this class to buffer
12565  * the keypress events for a certain number of milliseconds, and perform only if they stop
12566  * for that amount of time.
12567  * @constructor The parameters to this constructor serve as defaults and are not required.
12568  * @param {Function} fn (optional) The default function to timeout
12569  * @param {Object} scope (optional) The default scope of that timeout
12570  * @param {Array} args (optional) The default Array of arguments
12571  */
12572 Roo.util.DelayedTask = function(fn, scope, args){
12573     var id = null, d, t;
12574
12575     var call = function(){
12576         var now = new Date().getTime();
12577         if(now - t >= d){
12578             clearInterval(id);
12579             id = null;
12580             fn.apply(scope, args || []);
12581         }
12582     };
12583     /**
12584      * Cancels any pending timeout and queues a new one
12585      * @param {Number} delay The milliseconds to delay
12586      * @param {Function} newFn (optional) Overrides function passed to constructor
12587      * @param {Object} newScope (optional) Overrides scope passed to constructor
12588      * @param {Array} newArgs (optional) Overrides args passed to constructor
12589      */
12590     this.delay = function(delay, newFn, newScope, newArgs){
12591         if(id && delay != d){
12592             this.cancel();
12593         }
12594         d = delay;
12595         t = new Date().getTime();
12596         fn = newFn || fn;
12597         scope = newScope || scope;
12598         args = newArgs || args;
12599         if(!id){
12600             id = setInterval(call, d);
12601         }
12602     };
12603
12604     /**
12605      * Cancel the last queued timeout
12606      */
12607     this.cancel = function(){
12608         if(id){
12609             clearInterval(id);
12610             id = null;
12611         }
12612     };
12613 };/*
12614  * Based on:
12615  * Ext JS Library 1.1.1
12616  * Copyright(c) 2006-2007, Ext JS, LLC.
12617  *
12618  * Originally Released Under LGPL - original licence link has changed is not relivant.
12619  *
12620  * Fork - LGPL
12621  * <script type="text/javascript">
12622  */
12623  
12624  
12625 Roo.util.TaskRunner = function(interval){
12626     interval = interval || 10;
12627     var tasks = [], removeQueue = [];
12628     var id = 0;
12629     var running = false;
12630
12631     var stopThread = function(){
12632         running = false;
12633         clearInterval(id);
12634         id = 0;
12635     };
12636
12637     var startThread = function(){
12638         if(!running){
12639             running = true;
12640             id = setInterval(runTasks, interval);
12641         }
12642     };
12643
12644     var removeTask = function(task){
12645         removeQueue.push(task);
12646         if(task.onStop){
12647             task.onStop();
12648         }
12649     };
12650
12651     var runTasks = function(){
12652         if(removeQueue.length > 0){
12653             for(var i = 0, len = removeQueue.length; i < len; i++){
12654                 tasks.remove(removeQueue[i]);
12655             }
12656             removeQueue = [];
12657             if(tasks.length < 1){
12658                 stopThread();
12659                 return;
12660             }
12661         }
12662         var now = new Date().getTime();
12663         for(var i = 0, len = tasks.length; i < len; ++i){
12664             var t = tasks[i];
12665             var itime = now - t.taskRunTime;
12666             if(t.interval <= itime){
12667                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12668                 t.taskRunTime = now;
12669                 if(rt === false || t.taskRunCount === t.repeat){
12670                     removeTask(t);
12671                     return;
12672                 }
12673             }
12674             if(t.duration && t.duration <= (now - t.taskStartTime)){
12675                 removeTask(t);
12676             }
12677         }
12678     };
12679
12680     /**
12681      * Queues a new task.
12682      * @param {Object} task
12683      */
12684     this.start = function(task){
12685         tasks.push(task);
12686         task.taskStartTime = new Date().getTime();
12687         task.taskRunTime = 0;
12688         task.taskRunCount = 0;
12689         startThread();
12690         return task;
12691     };
12692
12693     this.stop = function(task){
12694         removeTask(task);
12695         return task;
12696     };
12697
12698     this.stopAll = function(){
12699         stopThread();
12700         for(var i = 0, len = tasks.length; i < len; i++){
12701             if(tasks[i].onStop){
12702                 tasks[i].onStop();
12703             }
12704         }
12705         tasks = [];
12706         removeQueue = [];
12707     };
12708 };
12709
12710 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12711  * Based on:
12712  * Ext JS Library 1.1.1
12713  * Copyright(c) 2006-2007, Ext JS, LLC.
12714  *
12715  * Originally Released Under LGPL - original licence link has changed is not relivant.
12716  *
12717  * Fork - LGPL
12718  * <script type="text/javascript">
12719  */
12720
12721  
12722 /**
12723  * @class Roo.util.MixedCollection
12724  * @extends Roo.util.Observable
12725  * A Collection class that maintains both numeric indexes and keys and exposes events.
12726  * @constructor
12727  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12728  * collection (defaults to false)
12729  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12730  * and return the key value for that item.  This is used when available to look up the key on items that
12731  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12732  * equivalent to providing an implementation for the {@link #getKey} method.
12733  */
12734 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12735     this.items = [];
12736     this.map = {};
12737     this.keys = [];
12738     this.length = 0;
12739     this.addEvents({
12740         /**
12741          * @event clear
12742          * Fires when the collection is cleared.
12743          */
12744         "clear" : true,
12745         /**
12746          * @event add
12747          * Fires when an item is added to the collection.
12748          * @param {Number} index The index at which the item was added.
12749          * @param {Object} o The item added.
12750          * @param {String} key The key associated with the added item.
12751          */
12752         "add" : true,
12753         /**
12754          * @event replace
12755          * Fires when an item is replaced in the collection.
12756          * @param {String} key he key associated with the new added.
12757          * @param {Object} old The item being replaced.
12758          * @param {Object} new The new item.
12759          */
12760         "replace" : true,
12761         /**
12762          * @event remove
12763          * Fires when an item is removed from the collection.
12764          * @param {Object} o The item being removed.
12765          * @param {String} key (optional) The key associated with the removed item.
12766          */
12767         "remove" : true,
12768         "sort" : true
12769     });
12770     this.allowFunctions = allowFunctions === true;
12771     if(keyFn){
12772         this.getKey = keyFn;
12773     }
12774     Roo.util.MixedCollection.superclass.constructor.call(this);
12775 };
12776
12777 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12778     allowFunctions : false,
12779     
12780 /**
12781  * Adds an item to the collection.
12782  * @param {String} key The key to associate with the item
12783  * @param {Object} o The item to add.
12784  * @return {Object} The item added.
12785  */
12786     add : function(key, o){
12787         if(arguments.length == 1){
12788             o = arguments[0];
12789             key = this.getKey(o);
12790         }
12791         if(typeof key == "undefined" || key === null){
12792             this.length++;
12793             this.items.push(o);
12794             this.keys.push(null);
12795         }else{
12796             var old = this.map[key];
12797             if(old){
12798                 return this.replace(key, o);
12799             }
12800             this.length++;
12801             this.items.push(o);
12802             this.map[key] = o;
12803             this.keys.push(key);
12804         }
12805         this.fireEvent("add", this.length-1, o, key);
12806         return o;
12807     },
12808        
12809 /**
12810   * MixedCollection has a generic way to fetch keys if you implement getKey.
12811 <pre><code>
12812 // normal way
12813 var mc = new Roo.util.MixedCollection();
12814 mc.add(someEl.dom.id, someEl);
12815 mc.add(otherEl.dom.id, otherEl);
12816 //and so on
12817
12818 // using getKey
12819 var mc = new Roo.util.MixedCollection();
12820 mc.getKey = function(el){
12821    return el.dom.id;
12822 };
12823 mc.add(someEl);
12824 mc.add(otherEl);
12825
12826 // or via the constructor
12827 var mc = new Roo.util.MixedCollection(false, function(el){
12828    return el.dom.id;
12829 });
12830 mc.add(someEl);
12831 mc.add(otherEl);
12832 </code></pre>
12833  * @param o {Object} The item for which to find the key.
12834  * @return {Object} The key for the passed item.
12835  */
12836     getKey : function(o){
12837          return o.id; 
12838     },
12839    
12840 /**
12841  * Replaces an item in the collection.
12842  * @param {String} key The key associated with the item to replace, or the item to replace.
12843  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12844  * @return {Object}  The new item.
12845  */
12846     replace : function(key, o){
12847         if(arguments.length == 1){
12848             o = arguments[0];
12849             key = this.getKey(o);
12850         }
12851         var old = this.item(key);
12852         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12853              return this.add(key, o);
12854         }
12855         var index = this.indexOfKey(key);
12856         this.items[index] = o;
12857         this.map[key] = o;
12858         this.fireEvent("replace", key, old, o);
12859         return o;
12860     },
12861    
12862 /**
12863  * Adds all elements of an Array or an Object to the collection.
12864  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12865  * an Array of values, each of which are added to the collection.
12866  */
12867     addAll : function(objs){
12868         if(arguments.length > 1 || objs instanceof Array){
12869             var args = arguments.length > 1 ? arguments : objs;
12870             for(var i = 0, len = args.length; i < len; i++){
12871                 this.add(args[i]);
12872             }
12873         }else{
12874             for(var key in objs){
12875                 if(this.allowFunctions || typeof objs[key] != "function"){
12876                     this.add(key, objs[key]);
12877                 }
12878             }
12879         }
12880     },
12881    
12882 /**
12883  * Executes the specified function once for every item in the collection, passing each
12884  * item as the first and only parameter. returning false from the function will stop the iteration.
12885  * @param {Function} fn The function to execute for each item.
12886  * @param {Object} scope (optional) The scope in which to execute the function.
12887  */
12888     each : function(fn, scope){
12889         var items = [].concat(this.items); // each safe for removal
12890         for(var i = 0, len = items.length; i < len; i++){
12891             if(fn.call(scope || items[i], items[i], i, len) === false){
12892                 break;
12893             }
12894         }
12895     },
12896    
12897 /**
12898  * Executes the specified function once for every key in the collection, passing each
12899  * key, and its associated item as the first two parameters.
12900  * @param {Function} fn The function to execute for each item.
12901  * @param {Object} scope (optional) The scope in which to execute the function.
12902  */
12903     eachKey : function(fn, scope){
12904         for(var i = 0, len = this.keys.length; i < len; i++){
12905             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12906         }
12907     },
12908    
12909 /**
12910  * Returns the first item in the collection which elicits a true return value from the
12911  * passed selection function.
12912  * @param {Function} fn The selection function to execute for each item.
12913  * @param {Object} scope (optional) The scope in which to execute the function.
12914  * @return {Object} The first item in the collection which returned true from the selection function.
12915  */
12916     find : function(fn, scope){
12917         for(var i = 0, len = this.items.length; i < len; i++){
12918             if(fn.call(scope || window, this.items[i], this.keys[i])){
12919                 return this.items[i];
12920             }
12921         }
12922         return null;
12923     },
12924    
12925 /**
12926  * Inserts an item at the specified index in the collection.
12927  * @param {Number} index The index to insert the item at.
12928  * @param {String} key The key to associate with the new item, or the item itself.
12929  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12930  * @return {Object} The item inserted.
12931  */
12932     insert : function(index, key, o){
12933         if(arguments.length == 2){
12934             o = arguments[1];
12935             key = this.getKey(o);
12936         }
12937         if(index >= this.length){
12938             return this.add(key, o);
12939         }
12940         this.length++;
12941         this.items.splice(index, 0, o);
12942         if(typeof key != "undefined" && key != null){
12943             this.map[key] = o;
12944         }
12945         this.keys.splice(index, 0, key);
12946         this.fireEvent("add", index, o, key);
12947         return o;
12948     },
12949    
12950 /**
12951  * Removed an item from the collection.
12952  * @param {Object} o The item to remove.
12953  * @return {Object} The item removed.
12954  */
12955     remove : function(o){
12956         return this.removeAt(this.indexOf(o));
12957     },
12958    
12959 /**
12960  * Remove an item from a specified index in the collection.
12961  * @param {Number} index The index within the collection of the item to remove.
12962  */
12963     removeAt : function(index){
12964         if(index < this.length && index >= 0){
12965             this.length--;
12966             var o = this.items[index];
12967             this.items.splice(index, 1);
12968             var key = this.keys[index];
12969             if(typeof key != "undefined"){
12970                 delete this.map[key];
12971             }
12972             this.keys.splice(index, 1);
12973             this.fireEvent("remove", o, key);
12974         }
12975     },
12976    
12977 /**
12978  * Removed an item associated with the passed key fom the collection.
12979  * @param {String} key The key of the item to remove.
12980  */
12981     removeKey : function(key){
12982         return this.removeAt(this.indexOfKey(key));
12983     },
12984    
12985 /**
12986  * Returns the number of items in the collection.
12987  * @return {Number} the number of items in the collection.
12988  */
12989     getCount : function(){
12990         return this.length; 
12991     },
12992    
12993 /**
12994  * Returns index within the collection of the passed Object.
12995  * @param {Object} o The item to find the index of.
12996  * @return {Number} index of the item.
12997  */
12998     indexOf : function(o){
12999         if(!this.items.indexOf){
13000             for(var i = 0, len = this.items.length; i < len; i++){
13001                 if(this.items[i] == o) return i;
13002             }
13003             return -1;
13004         }else{
13005             return this.items.indexOf(o);
13006         }
13007     },
13008    
13009 /**
13010  * Returns index within the collection of the passed key.
13011  * @param {String} key The key to find the index of.
13012  * @return {Number} index of the key.
13013  */
13014     indexOfKey : function(key){
13015         if(!this.keys.indexOf){
13016             for(var i = 0, len = this.keys.length; i < len; i++){
13017                 if(this.keys[i] == key) return i;
13018             }
13019             return -1;
13020         }else{
13021             return this.keys.indexOf(key);
13022         }
13023     },
13024    
13025 /**
13026  * Returns the item associated with the passed key OR index. Key has priority over index.
13027  * @param {String/Number} key The key or index of the item.
13028  * @return {Object} The item associated with the passed key.
13029  */
13030     item : function(key){
13031         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13032         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13033     },
13034     
13035 /**
13036  * Returns the item at the specified index.
13037  * @param {Number} index The index of the item.
13038  * @return {Object}
13039  */
13040     itemAt : function(index){
13041         return this.items[index];
13042     },
13043     
13044 /**
13045  * Returns the item associated with the passed key.
13046  * @param {String/Number} key The key of the item.
13047  * @return {Object} The item associated with the passed key.
13048  */
13049     key : function(key){
13050         return this.map[key];
13051     },
13052    
13053 /**
13054  * Returns true if the collection contains the passed Object as an item.
13055  * @param {Object} o  The Object to look for in the collection.
13056  * @return {Boolean} True if the collection contains the Object as an item.
13057  */
13058     contains : function(o){
13059         return this.indexOf(o) != -1;
13060     },
13061    
13062 /**
13063  * Returns true if the collection contains the passed Object as a key.
13064  * @param {String} key The key to look for in the collection.
13065  * @return {Boolean} True if the collection contains the Object as a key.
13066  */
13067     containsKey : function(key){
13068         return typeof this.map[key] != "undefined";
13069     },
13070    
13071 /**
13072  * Removes all items from the collection.
13073  */
13074     clear : function(){
13075         this.length = 0;
13076         this.items = [];
13077         this.keys = [];
13078         this.map = {};
13079         this.fireEvent("clear");
13080     },
13081    
13082 /**
13083  * Returns the first item in the collection.
13084  * @return {Object} the first item in the collection..
13085  */
13086     first : function(){
13087         return this.items[0]; 
13088     },
13089    
13090 /**
13091  * Returns the last item in the collection.
13092  * @return {Object} the last item in the collection..
13093  */
13094     last : function(){
13095         return this.items[this.length-1];   
13096     },
13097     
13098     _sort : function(property, dir, fn){
13099         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13100         fn = fn || function(a, b){
13101             return a-b;
13102         };
13103         var c = [], k = this.keys, items = this.items;
13104         for(var i = 0, len = items.length; i < len; i++){
13105             c[c.length] = {key: k[i], value: items[i], index: i};
13106         }
13107         c.sort(function(a, b){
13108             var v = fn(a[property], b[property]) * dsc;
13109             if(v == 0){
13110                 v = (a.index < b.index ? -1 : 1);
13111             }
13112             return v;
13113         });
13114         for(var i = 0, len = c.length; i < len; i++){
13115             items[i] = c[i].value;
13116             k[i] = c[i].key;
13117         }
13118         this.fireEvent("sort", this);
13119     },
13120     
13121     /**
13122      * Sorts this collection with the passed comparison function
13123      * @param {String} direction (optional) "ASC" or "DESC"
13124      * @param {Function} fn (optional) comparison function
13125      */
13126     sort : function(dir, fn){
13127         this._sort("value", dir, fn);
13128     },
13129     
13130     /**
13131      * Sorts this collection by keys
13132      * @param {String} direction (optional) "ASC" or "DESC"
13133      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13134      */
13135     keySort : function(dir, fn){
13136         this._sort("key", dir, fn || function(a, b){
13137             return String(a).toUpperCase()-String(b).toUpperCase();
13138         });
13139     },
13140     
13141     /**
13142      * Returns a range of items in this collection
13143      * @param {Number} startIndex (optional) defaults to 0
13144      * @param {Number} endIndex (optional) default to the last item
13145      * @return {Array} An array of items
13146      */
13147     getRange : function(start, end){
13148         var items = this.items;
13149         if(items.length < 1){
13150             return [];
13151         }
13152         start = start || 0;
13153         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13154         var r = [];
13155         if(start <= end){
13156             for(var i = start; i <= end; i++) {
13157                     r[r.length] = items[i];
13158             }
13159         }else{
13160             for(var i = start; i >= end; i--) {
13161                     r[r.length] = items[i];
13162             }
13163         }
13164         return r;
13165     },
13166         
13167     /**
13168      * Filter the <i>objects</i> in this collection by a specific property. 
13169      * Returns a new collection that has been filtered.
13170      * @param {String} property A property on your objects
13171      * @param {String/RegExp} value Either string that the property values 
13172      * should start with or a RegExp to test against the property
13173      * @return {MixedCollection} The new filtered collection
13174      */
13175     filter : function(property, value){
13176         if(!value.exec){ // not a regex
13177             value = String(value);
13178             if(value.length == 0){
13179                 return this.clone();
13180             }
13181             value = new RegExp("^" + Roo.escapeRe(value), "i");
13182         }
13183         return this.filterBy(function(o){
13184             return o && value.test(o[property]);
13185         });
13186         },
13187     
13188     /**
13189      * Filter by a function. * Returns a new collection that has been filtered.
13190      * The passed function will be called with each 
13191      * object in the collection. If the function returns true, the value is included 
13192      * otherwise it is filtered.
13193      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13194      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13195      * @return {MixedCollection} The new filtered collection
13196      */
13197     filterBy : function(fn, scope){
13198         var r = new Roo.util.MixedCollection();
13199         r.getKey = this.getKey;
13200         var k = this.keys, it = this.items;
13201         for(var i = 0, len = it.length; i < len; i++){
13202             if(fn.call(scope||this, it[i], k[i])){
13203                                 r.add(k[i], it[i]);
13204                         }
13205         }
13206         return r;
13207     },
13208     
13209     /**
13210      * Creates a duplicate of this collection
13211      * @return {MixedCollection}
13212      */
13213     clone : function(){
13214         var r = new Roo.util.MixedCollection();
13215         var k = this.keys, it = this.items;
13216         for(var i = 0, len = it.length; i < len; i++){
13217             r.add(k[i], it[i]);
13218         }
13219         r.getKey = this.getKey;
13220         return r;
13221     }
13222 });
13223 /**
13224  * Returns the item associated with the passed key or index.
13225  * @method
13226  * @param {String/Number} key The key or index of the item.
13227  * @return {Object} The item associated with the passed key.
13228  */
13229 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13230  * Based on:
13231  * Ext JS Library 1.1.1
13232  * Copyright(c) 2006-2007, Ext JS, LLC.
13233  *
13234  * Originally Released Under LGPL - original licence link has changed is not relivant.
13235  *
13236  * Fork - LGPL
13237  * <script type="text/javascript">
13238  */
13239 /**
13240  * @class Roo.util.JSON
13241  * Modified version of Douglas Crockford"s json.js that doesn"t
13242  * mess with the Object prototype 
13243  * http://www.json.org/js.html
13244  * @singleton
13245  */
13246 Roo.util.JSON = new (function(){
13247     var useHasOwn = {}.hasOwnProperty ? true : false;
13248     
13249     // crashes Safari in some instances
13250     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13251     
13252     var pad = function(n) {
13253         return n < 10 ? "0" + n : n;
13254     };
13255     
13256     var m = {
13257         "\b": '\\b',
13258         "\t": '\\t',
13259         "\n": '\\n',
13260         "\f": '\\f',
13261         "\r": '\\r',
13262         '"' : '\\"',
13263         "\\": '\\\\'
13264     };
13265
13266     var encodeString = function(s){
13267         if (/["\\\x00-\x1f]/.test(s)) {
13268             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13269                 var c = m[b];
13270                 if(c){
13271                     return c;
13272                 }
13273                 c = b.charCodeAt();
13274                 return "\\u00" +
13275                     Math.floor(c / 16).toString(16) +
13276                     (c % 16).toString(16);
13277             }) + '"';
13278         }
13279         return '"' + s + '"';
13280     };
13281     
13282     var encodeArray = function(o){
13283         var a = ["["], b, i, l = o.length, v;
13284             for (i = 0; i < l; i += 1) {
13285                 v = o[i];
13286                 switch (typeof v) {
13287                     case "undefined":
13288                     case "function":
13289                     case "unknown":
13290                         break;
13291                     default:
13292                         if (b) {
13293                             a.push(',');
13294                         }
13295                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13296                         b = true;
13297                 }
13298             }
13299             a.push("]");
13300             return a.join("");
13301     };
13302     
13303     var encodeDate = function(o){
13304         return '"' + o.getFullYear() + "-" +
13305                 pad(o.getMonth() + 1) + "-" +
13306                 pad(o.getDate()) + "T" +
13307                 pad(o.getHours()) + ":" +
13308                 pad(o.getMinutes()) + ":" +
13309                 pad(o.getSeconds()) + '"';
13310     };
13311     
13312     /**
13313      * Encodes an Object, Array or other value
13314      * @param {Mixed} o The variable to encode
13315      * @return {String} The JSON string
13316      */
13317     this.encode = function(o)
13318     {
13319         // should this be extended to fully wrap stringify..
13320         
13321         if(typeof o == "undefined" || o === null){
13322             return "null";
13323         }else if(o instanceof Array){
13324             return encodeArray(o);
13325         }else if(o instanceof Date){
13326             return encodeDate(o);
13327         }else if(typeof o == "string"){
13328             return encodeString(o);
13329         }else if(typeof o == "number"){
13330             return isFinite(o) ? String(o) : "null";
13331         }else if(typeof o == "boolean"){
13332             return String(o);
13333         }else {
13334             var a = ["{"], b, i, v;
13335             for (i in o) {
13336                 if(!useHasOwn || o.hasOwnProperty(i)) {
13337                     v = o[i];
13338                     switch (typeof v) {
13339                     case "undefined":
13340                     case "function":
13341                     case "unknown":
13342                         break;
13343                     default:
13344                         if(b){
13345                             a.push(',');
13346                         }
13347                         a.push(this.encode(i), ":",
13348                                 v === null ? "null" : this.encode(v));
13349                         b = true;
13350                     }
13351                 }
13352             }
13353             a.push("}");
13354             return a.join("");
13355         }
13356     };
13357     
13358     /**
13359      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13360      * @param {String} json The JSON string
13361      * @return {Object} The resulting object
13362      */
13363     this.decode = function(json){
13364         
13365         return  /** eval:var:json */ eval("(" + json + ')');
13366     };
13367 })();
13368 /** 
13369  * Shorthand for {@link Roo.util.JSON#encode}
13370  * @member Roo encode 
13371  * @method */
13372 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13373 /** 
13374  * Shorthand for {@link Roo.util.JSON#decode}
13375  * @member Roo decode 
13376  * @method */
13377 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13378 /*
13379  * Based on:
13380  * Ext JS Library 1.1.1
13381  * Copyright(c) 2006-2007, Ext JS, LLC.
13382  *
13383  * Originally Released Under LGPL - original licence link has changed is not relivant.
13384  *
13385  * Fork - LGPL
13386  * <script type="text/javascript">
13387  */
13388  
13389 /**
13390  * @class Roo.util.Format
13391  * Reusable data formatting functions
13392  * @singleton
13393  */
13394 Roo.util.Format = function(){
13395     var trimRe = /^\s+|\s+$/g;
13396     return {
13397         /**
13398          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13399          * @param {String} value The string to truncate
13400          * @param {Number} length The maximum length to allow before truncating
13401          * @return {String} The converted text
13402          */
13403         ellipsis : function(value, len){
13404             if(value && value.length > len){
13405                 return value.substr(0, len-3)+"...";
13406             }
13407             return value;
13408         },
13409
13410         /**
13411          * Checks a reference and converts it to empty string if it is undefined
13412          * @param {Mixed} value Reference to check
13413          * @return {Mixed} Empty string if converted, otherwise the original value
13414          */
13415         undef : function(value){
13416             return typeof value != "undefined" ? value : "";
13417         },
13418
13419         /**
13420          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13421          * @param {String} value The string to encode
13422          * @return {String} The encoded text
13423          */
13424         htmlEncode : function(value){
13425             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13426         },
13427
13428         /**
13429          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13430          * @param {String} value The string to decode
13431          * @return {String} The decoded text
13432          */
13433         htmlDecode : function(value){
13434             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13435         },
13436
13437         /**
13438          * Trims any whitespace from either side of a string
13439          * @param {String} value The text to trim
13440          * @return {String} The trimmed text
13441          */
13442         trim : function(value){
13443             return String(value).replace(trimRe, "");
13444         },
13445
13446         /**
13447          * Returns a substring from within an original string
13448          * @param {String} value The original text
13449          * @param {Number} start The start index of the substring
13450          * @param {Number} length The length of the substring
13451          * @return {String} The substring
13452          */
13453         substr : function(value, start, length){
13454             return String(value).substr(start, length);
13455         },
13456
13457         /**
13458          * Converts a string to all lower case letters
13459          * @param {String} value The text to convert
13460          * @return {String} The converted text
13461          */
13462         lowercase : function(value){
13463             return String(value).toLowerCase();
13464         },
13465
13466         /**
13467          * Converts a string to all upper case letters
13468          * @param {String} value The text to convert
13469          * @return {String} The converted text
13470          */
13471         uppercase : function(value){
13472             return String(value).toUpperCase();
13473         },
13474
13475         /**
13476          * Converts the first character only of a string to upper case
13477          * @param {String} value The text to convert
13478          * @return {String} The converted text
13479          */
13480         capitalize : function(value){
13481             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13482         },
13483
13484         // private
13485         call : function(value, fn){
13486             if(arguments.length > 2){
13487                 var args = Array.prototype.slice.call(arguments, 2);
13488                 args.unshift(value);
13489                  
13490                 return /** eval:var:value */  eval(fn).apply(window, args);
13491             }else{
13492                 /** eval:var:value */
13493                 return /** eval:var:value */ eval(fn).call(window, value);
13494             }
13495         },
13496
13497        
13498         /**
13499          * safer version of Math.toFixed..??/
13500          * @param {Number/String} value The numeric value to format
13501          * @param {Number/String} value Decimal places 
13502          * @return {String} The formatted currency string
13503          */
13504         toFixed : function(v, n)
13505         {
13506             // why not use to fixed - precision is buggered???
13507             if (!n) {
13508                 return Math.round(v-0);
13509             }
13510             var fact = Math.pow(10,n+1);
13511             v = (Math.round((v-0)*fact))/fact;
13512             var z = (''+fact).substring(2);
13513             if (v == Math.floor(v)) {
13514                 return Math.floor(v) + '.' + z;
13515             }
13516             
13517             // now just padd decimals..
13518             var ps = String(v).split('.');
13519             var fd = (ps[1] + z);
13520             var r = fd.substring(0,n); 
13521             var rm = fd.substring(n); 
13522             if (rm < 5) {
13523                 return ps[0] + '.' + r;
13524             }
13525             r*=1; // turn it into a number;
13526             r++;
13527             if (String(r).length != n) {
13528                 ps[0]*=1;
13529                 ps[0]++;
13530                 r = String(r).substring(1); // chop the end off.
13531             }
13532             
13533             return ps[0] + '.' + r;
13534              
13535         },
13536         
13537         /**
13538          * Format a number as US currency
13539          * @param {Number/String} value The numeric value to format
13540          * @return {String} The formatted currency string
13541          */
13542         usMoney : function(v){
13543             return '$' + Roo.util.Format.number(v);
13544         },
13545         
13546         /**
13547          * Format a number
13548          * eventually this should probably emulate php's number_format
13549          * @param {Number/String} value The numeric value to format
13550          * @param {Number} decimals number of decimal places
13551          * @return {String} The formatted currency string
13552          */
13553         number : function(v,decimals)
13554         {
13555             // multiply and round.
13556             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13557             var mul = Math.pow(10, decimals);
13558             var zero = String(mul).substring(1);
13559             v = (Math.round((v-0)*mul))/mul;
13560             
13561             // if it's '0' number.. then
13562             
13563             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13564             v = String(v);
13565             var ps = v.split('.');
13566             var whole = ps[0];
13567             
13568             
13569             var r = /(\d+)(\d{3})/;
13570             // add comma's
13571             while (r.test(whole)) {
13572                 whole = whole.replace(r, '$1' + ',' + '$2');
13573             }
13574             
13575             
13576             var sub = ps[1] ?
13577                     // has decimals..
13578                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13579                     // does not have decimals
13580                     (decimals ? ('.' + zero) : '');
13581             
13582             
13583             return whole + sub ;
13584         },
13585         
13586         /**
13587          * Parse a value into a formatted date using the specified format pattern.
13588          * @param {Mixed} value The value to format
13589          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13590          * @return {String} The formatted date string
13591          */
13592         date : function(v, format){
13593             if(!v){
13594                 return "";
13595             }
13596             if(!(v instanceof Date)){
13597                 v = new Date(Date.parse(v));
13598             }
13599             return v.dateFormat(format || "m/d/Y");
13600         },
13601
13602         /**
13603          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13604          * @param {String} format Any valid date format string
13605          * @return {Function} The date formatting function
13606          */
13607         dateRenderer : function(format){
13608             return function(v){
13609                 return Roo.util.Format.date(v, format);  
13610             };
13611         },
13612
13613         // private
13614         stripTagsRE : /<\/?[^>]+>/gi,
13615         
13616         /**
13617          * Strips all HTML tags
13618          * @param {Mixed} value The text from which to strip tags
13619          * @return {String} The stripped text
13620          */
13621         stripTags : function(v){
13622             return !v ? v : String(v).replace(this.stripTagsRE, "");
13623         }
13624     };
13625 }();/*
13626  * Based on:
13627  * Ext JS Library 1.1.1
13628  * Copyright(c) 2006-2007, Ext JS, LLC.
13629  *
13630  * Originally Released Under LGPL - original licence link has changed is not relivant.
13631  *
13632  * Fork - LGPL
13633  * <script type="text/javascript">
13634  */
13635
13636
13637  
13638
13639 /**
13640  * @class Roo.MasterTemplate
13641  * @extends Roo.Template
13642  * Provides a template that can have child templates. The syntax is:
13643 <pre><code>
13644 var t = new Roo.MasterTemplate(
13645         '&lt;select name="{name}"&gt;',
13646                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13647         '&lt;/select&gt;'
13648 );
13649 t.add('options', {value: 'foo', text: 'bar'});
13650 // or you can add multiple child elements in one shot
13651 t.addAll('options', [
13652     {value: 'foo', text: 'bar'},
13653     {value: 'foo2', text: 'bar2'},
13654     {value: 'foo3', text: 'bar3'}
13655 ]);
13656 // then append, applying the master template values
13657 t.append('my-form', {name: 'my-select'});
13658 </code></pre>
13659 * A name attribute for the child template is not required if you have only one child
13660 * template or you want to refer to them by index.
13661  */
13662 Roo.MasterTemplate = function(){
13663     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13664     this.originalHtml = this.html;
13665     var st = {};
13666     var m, re = this.subTemplateRe;
13667     re.lastIndex = 0;
13668     var subIndex = 0;
13669     while(m = re.exec(this.html)){
13670         var name = m[1], content = m[2];
13671         st[subIndex] = {
13672             name: name,
13673             index: subIndex,
13674             buffer: [],
13675             tpl : new Roo.Template(content)
13676         };
13677         if(name){
13678             st[name] = st[subIndex];
13679         }
13680         st[subIndex].tpl.compile();
13681         st[subIndex].tpl.call = this.call.createDelegate(this);
13682         subIndex++;
13683     }
13684     this.subCount = subIndex;
13685     this.subs = st;
13686 };
13687 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13688     /**
13689     * The regular expression used to match sub templates
13690     * @type RegExp
13691     * @property
13692     */
13693     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13694
13695     /**
13696      * Applies the passed values to a child template.
13697      * @param {String/Number} name (optional) The name or index of the child template
13698      * @param {Array/Object} values The values to be applied to the template
13699      * @return {MasterTemplate} this
13700      */
13701      add : function(name, values){
13702         if(arguments.length == 1){
13703             values = arguments[0];
13704             name = 0;
13705         }
13706         var s = this.subs[name];
13707         s.buffer[s.buffer.length] = s.tpl.apply(values);
13708         return this;
13709     },
13710
13711     /**
13712      * Applies all the passed values to a child template.
13713      * @param {String/Number} name (optional) The name or index of the child template
13714      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13715      * @param {Boolean} reset (optional) True to reset the template first
13716      * @return {MasterTemplate} this
13717      */
13718     fill : function(name, values, reset){
13719         var a = arguments;
13720         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13721             values = a[0];
13722             name = 0;
13723             reset = a[1];
13724         }
13725         if(reset){
13726             this.reset();
13727         }
13728         for(var i = 0, len = values.length; i < len; i++){
13729             this.add(name, values[i]);
13730         }
13731         return this;
13732     },
13733
13734     /**
13735      * Resets the template for reuse
13736      * @return {MasterTemplate} this
13737      */
13738      reset : function(){
13739         var s = this.subs;
13740         for(var i = 0; i < this.subCount; i++){
13741             s[i].buffer = [];
13742         }
13743         return this;
13744     },
13745
13746     applyTemplate : function(values){
13747         var s = this.subs;
13748         var replaceIndex = -1;
13749         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13750             return s[++replaceIndex].buffer.join("");
13751         });
13752         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13753     },
13754
13755     apply : function(){
13756         return this.applyTemplate.apply(this, arguments);
13757     },
13758
13759     compile : function(){return this;}
13760 });
13761
13762 /**
13763  * Alias for fill().
13764  * @method
13765  */
13766 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13767  /**
13768  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13769  * var tpl = Roo.MasterTemplate.from('element-id');
13770  * @param {String/HTMLElement} el
13771  * @param {Object} config
13772  * @static
13773  */
13774 Roo.MasterTemplate.from = function(el, config){
13775     el = Roo.getDom(el);
13776     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13777 };/*
13778  * Based on:
13779  * Ext JS Library 1.1.1
13780  * Copyright(c) 2006-2007, Ext JS, LLC.
13781  *
13782  * Originally Released Under LGPL - original licence link has changed is not relivant.
13783  *
13784  * Fork - LGPL
13785  * <script type="text/javascript">
13786  */
13787
13788  
13789 /**
13790  * @class Roo.util.CSS
13791  * Utility class for manipulating CSS rules
13792  * @singleton
13793  */
13794 Roo.util.CSS = function(){
13795         var rules = null;
13796         var doc = document;
13797
13798     var camelRe = /(-[a-z])/gi;
13799     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13800
13801    return {
13802    /**
13803     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13804     * tag and appended to the HEAD of the document.
13805     * @param {String|Object} cssText The text containing the css rules
13806     * @param {String} id An id to add to the stylesheet for later removal
13807     * @return {StyleSheet}
13808     */
13809     createStyleSheet : function(cssText, id){
13810         var ss;
13811         var head = doc.getElementsByTagName("head")[0];
13812         var nrules = doc.createElement("style");
13813         nrules.setAttribute("type", "text/css");
13814         if(id){
13815             nrules.setAttribute("id", id);
13816         }
13817         if (typeof(cssText) != 'string') {
13818             // support object maps..
13819             // not sure if this a good idea.. 
13820             // perhaps it should be merged with the general css handling
13821             // and handle js style props.
13822             var cssTextNew = [];
13823             for(var n in cssText) {
13824                 var citems = [];
13825                 for(var k in cssText[n]) {
13826                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13827                 }
13828                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13829                 
13830             }
13831             cssText = cssTextNew.join("\n");
13832             
13833         }
13834        
13835        
13836        if(Roo.isIE){
13837            head.appendChild(nrules);
13838            ss = nrules.styleSheet;
13839            ss.cssText = cssText;
13840        }else{
13841            try{
13842                 nrules.appendChild(doc.createTextNode(cssText));
13843            }catch(e){
13844                nrules.cssText = cssText; 
13845            }
13846            head.appendChild(nrules);
13847            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13848        }
13849        this.cacheStyleSheet(ss);
13850        return ss;
13851    },
13852
13853    /**
13854     * Removes a style or link tag by id
13855     * @param {String} id The id of the tag
13856     */
13857    removeStyleSheet : function(id){
13858        var existing = doc.getElementById(id);
13859        if(existing){
13860            existing.parentNode.removeChild(existing);
13861        }
13862    },
13863
13864    /**
13865     * Dynamically swaps an existing stylesheet reference for a new one
13866     * @param {String} id The id of an existing link tag to remove
13867     * @param {String} url The href of the new stylesheet to include
13868     */
13869    swapStyleSheet : function(id, url){
13870        this.removeStyleSheet(id);
13871        var ss = doc.createElement("link");
13872        ss.setAttribute("rel", "stylesheet");
13873        ss.setAttribute("type", "text/css");
13874        ss.setAttribute("id", id);
13875        ss.setAttribute("href", url);
13876        doc.getElementsByTagName("head")[0].appendChild(ss);
13877    },
13878    
13879    /**
13880     * Refresh the rule cache if you have dynamically added stylesheets
13881     * @return {Object} An object (hash) of rules indexed by selector
13882     */
13883    refreshCache : function(){
13884        return this.getRules(true);
13885    },
13886
13887    // private
13888    cacheStyleSheet : function(stylesheet){
13889        if(!rules){
13890            rules = {};
13891        }
13892        try{// try catch for cross domain access issue
13893            var ssRules = stylesheet.cssRules || stylesheet.rules;
13894            for(var j = ssRules.length-1; j >= 0; --j){
13895                rules[ssRules[j].selectorText] = ssRules[j];
13896            }
13897        }catch(e){}
13898    },
13899    
13900    /**
13901     * Gets all css rules for the document
13902     * @param {Boolean} refreshCache true to refresh the internal cache
13903     * @return {Object} An object (hash) of rules indexed by selector
13904     */
13905    getRules : function(refreshCache){
13906                 if(rules == null || refreshCache){
13907                         rules = {};
13908                         var ds = doc.styleSheets;
13909                         for(var i =0, len = ds.length; i < len; i++){
13910                             try{
13911                         this.cacheStyleSheet(ds[i]);
13912                     }catch(e){} 
13913                 }
13914                 }
13915                 return rules;
13916         },
13917         
13918         /**
13919     * Gets an an individual CSS rule by selector(s)
13920     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13921     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13922     * @return {CSSRule} The CSS rule or null if one is not found
13923     */
13924    getRule : function(selector, refreshCache){
13925                 var rs = this.getRules(refreshCache);
13926                 if(!(selector instanceof Array)){
13927                     return rs[selector];
13928                 }
13929                 for(var i = 0; i < selector.length; i++){
13930                         if(rs[selector[i]]){
13931                                 return rs[selector[i]];
13932                         }
13933                 }
13934                 return null;
13935         },
13936         
13937         
13938         /**
13939     * Updates a rule property
13940     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13941     * @param {String} property The css property
13942     * @param {String} value The new value for the property
13943     * @return {Boolean} true If a rule was found and updated
13944     */
13945    updateRule : function(selector, property, value){
13946                 if(!(selector instanceof Array)){
13947                         var rule = this.getRule(selector);
13948                         if(rule){
13949                                 rule.style[property.replace(camelRe, camelFn)] = value;
13950                                 return true;
13951                         }
13952                 }else{
13953                         for(var i = 0; i < selector.length; i++){
13954                                 if(this.updateRule(selector[i], property, value)){
13955                                         return true;
13956                                 }
13957                         }
13958                 }
13959                 return false;
13960         }
13961    };   
13962 }();/*
13963  * Based on:
13964  * Ext JS Library 1.1.1
13965  * Copyright(c) 2006-2007, Ext JS, LLC.
13966  *
13967  * Originally Released Under LGPL - original licence link has changed is not relivant.
13968  *
13969  * Fork - LGPL
13970  * <script type="text/javascript">
13971  */
13972
13973  
13974
13975 /**
13976  * @class Roo.util.ClickRepeater
13977  * @extends Roo.util.Observable
13978  * 
13979  * A wrapper class which can be applied to any element. Fires a "click" event while the
13980  * mouse is pressed. The interval between firings may be specified in the config but
13981  * defaults to 10 milliseconds.
13982  * 
13983  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13984  * 
13985  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13986  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13987  * Similar to an autorepeat key delay.
13988  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13989  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13990  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13991  *           "interval" and "delay" are ignored. "immediate" is honored.
13992  * @cfg {Boolean} preventDefault True to prevent the default click event
13993  * @cfg {Boolean} stopDefault True to stop the default click event
13994  * 
13995  * @history
13996  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13997  *     2007-02-02 jvs Renamed to ClickRepeater
13998  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13999  *
14000  *  @constructor
14001  * @param {String/HTMLElement/Element} el The element to listen on
14002  * @param {Object} config
14003  **/
14004 Roo.util.ClickRepeater = function(el, config)
14005 {
14006     this.el = Roo.get(el);
14007     this.el.unselectable();
14008
14009     Roo.apply(this, config);
14010
14011     this.addEvents({
14012     /**
14013      * @event mousedown
14014      * Fires when the mouse button is depressed.
14015      * @param {Roo.util.ClickRepeater} this
14016      */
14017         "mousedown" : true,
14018     /**
14019      * @event click
14020      * Fires on a specified interval during the time the element is pressed.
14021      * @param {Roo.util.ClickRepeater} this
14022      */
14023         "click" : true,
14024     /**
14025      * @event mouseup
14026      * Fires when the mouse key is released.
14027      * @param {Roo.util.ClickRepeater} this
14028      */
14029         "mouseup" : true
14030     });
14031
14032     this.el.on("mousedown", this.handleMouseDown, this);
14033     if(this.preventDefault || this.stopDefault){
14034         this.el.on("click", function(e){
14035             if(this.preventDefault){
14036                 e.preventDefault();
14037             }
14038             if(this.stopDefault){
14039                 e.stopEvent();
14040             }
14041         }, this);
14042     }
14043
14044     // allow inline handler
14045     if(this.handler){
14046         this.on("click", this.handler,  this.scope || this);
14047     }
14048
14049     Roo.util.ClickRepeater.superclass.constructor.call(this);
14050 };
14051
14052 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14053     interval : 20,
14054     delay: 250,
14055     preventDefault : true,
14056     stopDefault : false,
14057     timer : 0,
14058
14059     // private
14060     handleMouseDown : function(){
14061         clearTimeout(this.timer);
14062         this.el.blur();
14063         if(this.pressClass){
14064             this.el.addClass(this.pressClass);
14065         }
14066         this.mousedownTime = new Date();
14067
14068         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14069         this.el.on("mouseout", this.handleMouseOut, this);
14070
14071         this.fireEvent("mousedown", this);
14072         this.fireEvent("click", this);
14073         
14074         this.timer = this.click.defer(this.delay || this.interval, this);
14075     },
14076
14077     // private
14078     click : function(){
14079         this.fireEvent("click", this);
14080         this.timer = this.click.defer(this.getInterval(), this);
14081     },
14082
14083     // private
14084     getInterval: function(){
14085         if(!this.accelerate){
14086             return this.interval;
14087         }
14088         var pressTime = this.mousedownTime.getElapsed();
14089         if(pressTime < 500){
14090             return 400;
14091         }else if(pressTime < 1700){
14092             return 320;
14093         }else if(pressTime < 2600){
14094             return 250;
14095         }else if(pressTime < 3500){
14096             return 180;
14097         }else if(pressTime < 4400){
14098             return 140;
14099         }else if(pressTime < 5300){
14100             return 80;
14101         }else if(pressTime < 6200){
14102             return 50;
14103         }else{
14104             return 10;
14105         }
14106     },
14107
14108     // private
14109     handleMouseOut : function(){
14110         clearTimeout(this.timer);
14111         if(this.pressClass){
14112             this.el.removeClass(this.pressClass);
14113         }
14114         this.el.on("mouseover", this.handleMouseReturn, this);
14115     },
14116
14117     // private
14118     handleMouseReturn : function(){
14119         this.el.un("mouseover", this.handleMouseReturn);
14120         if(this.pressClass){
14121             this.el.addClass(this.pressClass);
14122         }
14123         this.click();
14124     },
14125
14126     // private
14127     handleMouseUp : function(){
14128         clearTimeout(this.timer);
14129         this.el.un("mouseover", this.handleMouseReturn);
14130         this.el.un("mouseout", this.handleMouseOut);
14131         Roo.get(document).un("mouseup", this.handleMouseUp);
14132         this.el.removeClass(this.pressClass);
14133         this.fireEvent("mouseup", this);
14134     }
14135 });/*
14136  * Based on:
14137  * Ext JS Library 1.1.1
14138  * Copyright(c) 2006-2007, Ext JS, LLC.
14139  *
14140  * Originally Released Under LGPL - original licence link has changed is not relivant.
14141  *
14142  * Fork - LGPL
14143  * <script type="text/javascript">
14144  */
14145
14146  
14147 /**
14148  * @class Roo.KeyNav
14149  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14150  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14151  * way to implement custom navigation schemes for any UI component.</p>
14152  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14153  * pageUp, pageDown, del, home, end.  Usage:</p>
14154  <pre><code>
14155 var nav = new Roo.KeyNav("my-element", {
14156     "left" : function(e){
14157         this.moveLeft(e.ctrlKey);
14158     },
14159     "right" : function(e){
14160         this.moveRight(e.ctrlKey);
14161     },
14162     "enter" : function(e){
14163         this.save();
14164     },
14165     scope : this
14166 });
14167 </code></pre>
14168  * @constructor
14169  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14170  * @param {Object} config The config
14171  */
14172 Roo.KeyNav = function(el, config){
14173     this.el = Roo.get(el);
14174     Roo.apply(this, config);
14175     if(!this.disabled){
14176         this.disabled = true;
14177         this.enable();
14178     }
14179 };
14180
14181 Roo.KeyNav.prototype = {
14182     /**
14183      * @cfg {Boolean} disabled
14184      * True to disable this KeyNav instance (defaults to false)
14185      */
14186     disabled : false,
14187     /**
14188      * @cfg {String} defaultEventAction
14189      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14190      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14191      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14192      */
14193     defaultEventAction: "stopEvent",
14194     /**
14195      * @cfg {Boolean} forceKeyDown
14196      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14197      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14198      * handle keydown instead of keypress.
14199      */
14200     forceKeyDown : false,
14201
14202     // private
14203     prepareEvent : function(e){
14204         var k = e.getKey();
14205         var h = this.keyToHandler[k];
14206         //if(h && this[h]){
14207         //    e.stopPropagation();
14208         //}
14209         if(Roo.isSafari && h && k >= 37 && k <= 40){
14210             e.stopEvent();
14211         }
14212     },
14213
14214     // private
14215     relay : function(e){
14216         var k = e.getKey();
14217         var h = this.keyToHandler[k];
14218         if(h && this[h]){
14219             if(this.doRelay(e, this[h], h) !== true){
14220                 e[this.defaultEventAction]();
14221             }
14222         }
14223     },
14224
14225     // private
14226     doRelay : function(e, h, hname){
14227         return h.call(this.scope || this, e);
14228     },
14229
14230     // possible handlers
14231     enter : false,
14232     left : false,
14233     right : false,
14234     up : false,
14235     down : false,
14236     tab : false,
14237     esc : false,
14238     pageUp : false,
14239     pageDown : false,
14240     del : false,
14241     home : false,
14242     end : false,
14243
14244     // quick lookup hash
14245     keyToHandler : {
14246         37 : "left",
14247         39 : "right",
14248         38 : "up",
14249         40 : "down",
14250         33 : "pageUp",
14251         34 : "pageDown",
14252         46 : "del",
14253         36 : "home",
14254         35 : "end",
14255         13 : "enter",
14256         27 : "esc",
14257         9  : "tab"
14258     },
14259
14260         /**
14261          * Enable this KeyNav
14262          */
14263         enable: function(){
14264                 if(this.disabled){
14265             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14266             // the EventObject will normalize Safari automatically
14267             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14268                 this.el.on("keydown", this.relay,  this);
14269             }else{
14270                 this.el.on("keydown", this.prepareEvent,  this);
14271                 this.el.on("keypress", this.relay,  this);
14272             }
14273                     this.disabled = false;
14274                 }
14275         },
14276
14277         /**
14278          * Disable this KeyNav
14279          */
14280         disable: function(){
14281                 if(!this.disabled){
14282                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14283                 this.el.un("keydown", this.relay);
14284             }else{
14285                 this.el.un("keydown", this.prepareEvent);
14286                 this.el.un("keypress", this.relay);
14287             }
14288                     this.disabled = true;
14289                 }
14290         }
14291 };/*
14292  * Based on:
14293  * Ext JS Library 1.1.1
14294  * Copyright(c) 2006-2007, Ext JS, LLC.
14295  *
14296  * Originally Released Under LGPL - original licence link has changed is not relivant.
14297  *
14298  * Fork - LGPL
14299  * <script type="text/javascript">
14300  */
14301
14302  
14303 /**
14304  * @class Roo.KeyMap
14305  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14306  * The constructor accepts the same config object as defined by {@link #addBinding}.
14307  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14308  * combination it will call the function with this signature (if the match is a multi-key
14309  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14310  * A KeyMap can also handle a string representation of keys.<br />
14311  * Usage:
14312  <pre><code>
14313 // map one key by key code
14314 var map = new Roo.KeyMap("my-element", {
14315     key: 13, // or Roo.EventObject.ENTER
14316     fn: myHandler,
14317     scope: myObject
14318 });
14319
14320 // map multiple keys to one action by string
14321 var map = new Roo.KeyMap("my-element", {
14322     key: "a\r\n\t",
14323     fn: myHandler,
14324     scope: myObject
14325 });
14326
14327 // map multiple keys to multiple actions by strings and array of codes
14328 var map = new Roo.KeyMap("my-element", [
14329     {
14330         key: [10,13],
14331         fn: function(){ alert("Return was pressed"); }
14332     }, {
14333         key: "abc",
14334         fn: function(){ alert('a, b or c was pressed'); }
14335     }, {
14336         key: "\t",
14337         ctrl:true,
14338         shift:true,
14339         fn: function(){ alert('Control + shift + tab was pressed.'); }
14340     }
14341 ]);
14342 </code></pre>
14343  * <b>Note: A KeyMap starts enabled</b>
14344  * @constructor
14345  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14346  * @param {Object} config The config (see {@link #addBinding})
14347  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14348  */
14349 Roo.KeyMap = function(el, config, eventName){
14350     this.el  = Roo.get(el);
14351     this.eventName = eventName || "keydown";
14352     this.bindings = [];
14353     if(config){
14354         this.addBinding(config);
14355     }
14356     this.enable();
14357 };
14358
14359 Roo.KeyMap.prototype = {
14360     /**
14361      * True to stop the event from bubbling and prevent the default browser action if the
14362      * key was handled by the KeyMap (defaults to false)
14363      * @type Boolean
14364      */
14365     stopEvent : false,
14366
14367     /**
14368      * Add a new binding to this KeyMap. The following config object properties are supported:
14369      * <pre>
14370 Property    Type             Description
14371 ----------  ---------------  ----------------------------------------------------------------------
14372 key         String/Array     A single keycode or an array of keycodes to handle
14373 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14374 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14375 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14376 fn          Function         The function to call when KeyMap finds the expected key combination
14377 scope       Object           The scope of the callback function
14378 </pre>
14379      *
14380      * Usage:
14381      * <pre><code>
14382 // Create a KeyMap
14383 var map = new Roo.KeyMap(document, {
14384     key: Roo.EventObject.ENTER,
14385     fn: handleKey,
14386     scope: this
14387 });
14388
14389 //Add a new binding to the existing KeyMap later
14390 map.addBinding({
14391     key: 'abc',
14392     shift: true,
14393     fn: handleKey,
14394     scope: this
14395 });
14396 </code></pre>
14397      * @param {Object/Array} config A single KeyMap config or an array of configs
14398      */
14399         addBinding : function(config){
14400         if(config instanceof Array){
14401             for(var i = 0, len = config.length; i < len; i++){
14402                 this.addBinding(config[i]);
14403             }
14404             return;
14405         }
14406         var keyCode = config.key,
14407             shift = config.shift, 
14408             ctrl = config.ctrl, 
14409             alt = config.alt,
14410             fn = config.fn,
14411             scope = config.scope;
14412         if(typeof keyCode == "string"){
14413             var ks = [];
14414             var keyString = keyCode.toUpperCase();
14415             for(var j = 0, len = keyString.length; j < len; j++){
14416                 ks.push(keyString.charCodeAt(j));
14417             }
14418             keyCode = ks;
14419         }
14420         var keyArray = keyCode instanceof Array;
14421         var handler = function(e){
14422             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14423                 var k = e.getKey();
14424                 if(keyArray){
14425                     for(var i = 0, len = keyCode.length; i < len; i++){
14426                         if(keyCode[i] == k){
14427                           if(this.stopEvent){
14428                               e.stopEvent();
14429                           }
14430                           fn.call(scope || window, k, e);
14431                           return;
14432                         }
14433                     }
14434                 }else{
14435                     if(k == keyCode){
14436                         if(this.stopEvent){
14437                            e.stopEvent();
14438                         }
14439                         fn.call(scope || window, k, e);
14440                     }
14441                 }
14442             }
14443         };
14444         this.bindings.push(handler);  
14445         },
14446
14447     /**
14448      * Shorthand for adding a single key listener
14449      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14450      * following options:
14451      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14452      * @param {Function} fn The function to call
14453      * @param {Object} scope (optional) The scope of the function
14454      */
14455     on : function(key, fn, scope){
14456         var keyCode, shift, ctrl, alt;
14457         if(typeof key == "object" && !(key instanceof Array)){
14458             keyCode = key.key;
14459             shift = key.shift;
14460             ctrl = key.ctrl;
14461             alt = key.alt;
14462         }else{
14463             keyCode = key;
14464         }
14465         this.addBinding({
14466             key: keyCode,
14467             shift: shift,
14468             ctrl: ctrl,
14469             alt: alt,
14470             fn: fn,
14471             scope: scope
14472         })
14473     },
14474
14475     // private
14476     handleKeyDown : function(e){
14477             if(this.enabled){ //just in case
14478             var b = this.bindings;
14479             for(var i = 0, len = b.length; i < len; i++){
14480                 b[i].call(this, e);
14481             }
14482             }
14483         },
14484         
14485         /**
14486          * Returns true if this KeyMap is enabled
14487          * @return {Boolean} 
14488          */
14489         isEnabled : function(){
14490             return this.enabled;  
14491         },
14492         
14493         /**
14494          * Enables this KeyMap
14495          */
14496         enable: function(){
14497                 if(!this.enabled){
14498                     this.el.on(this.eventName, this.handleKeyDown, this);
14499                     this.enabled = true;
14500                 }
14501         },
14502
14503         /**
14504          * Disable this KeyMap
14505          */
14506         disable: function(){
14507                 if(this.enabled){
14508                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14509                     this.enabled = false;
14510                 }
14511         }
14512 };/*
14513  * Based on:
14514  * Ext JS Library 1.1.1
14515  * Copyright(c) 2006-2007, Ext JS, LLC.
14516  *
14517  * Originally Released Under LGPL - original licence link has changed is not relivant.
14518  *
14519  * Fork - LGPL
14520  * <script type="text/javascript">
14521  */
14522
14523  
14524 /**
14525  * @class Roo.util.TextMetrics
14526  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14527  * wide, in pixels, a given block of text will be.
14528  * @singleton
14529  */
14530 Roo.util.TextMetrics = function(){
14531     var shared;
14532     return {
14533         /**
14534          * Measures the size of the specified text
14535          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14536          * that can affect the size of the rendered text
14537          * @param {String} text The text to measure
14538          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14539          * in order to accurately measure the text height
14540          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14541          */
14542         measure : function(el, text, fixedWidth){
14543             if(!shared){
14544                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14545             }
14546             shared.bind(el);
14547             shared.setFixedWidth(fixedWidth || 'auto');
14548             return shared.getSize(text);
14549         },
14550
14551         /**
14552          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14553          * the overhead of multiple calls to initialize the style properties on each measurement.
14554          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14555          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14556          * in order to accurately measure the text height
14557          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14558          */
14559         createInstance : function(el, fixedWidth){
14560             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14561         }
14562     };
14563 }();
14564
14565  
14566
14567 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14568     var ml = new Roo.Element(document.createElement('div'));
14569     document.body.appendChild(ml.dom);
14570     ml.position('absolute');
14571     ml.setLeftTop(-1000, -1000);
14572     ml.hide();
14573
14574     if(fixedWidth){
14575         ml.setWidth(fixedWidth);
14576     }
14577      
14578     var instance = {
14579         /**
14580          * Returns the size of the specified text based on the internal element's style and width properties
14581          * @memberOf Roo.util.TextMetrics.Instance#
14582          * @param {String} text The text to measure
14583          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14584          */
14585         getSize : function(text){
14586             ml.update(text);
14587             var s = ml.getSize();
14588             ml.update('');
14589             return s;
14590         },
14591
14592         /**
14593          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14594          * that can affect the size of the rendered text
14595          * @memberOf Roo.util.TextMetrics.Instance#
14596          * @param {String/HTMLElement} el The element, dom node or id
14597          */
14598         bind : function(el){
14599             ml.setStyle(
14600                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14601             );
14602         },
14603
14604         /**
14605          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14606          * to set a fixed width in order to accurately measure the text height.
14607          * @memberOf Roo.util.TextMetrics.Instance#
14608          * @param {Number} width The width to set on the element
14609          */
14610         setFixedWidth : function(width){
14611             ml.setWidth(width);
14612         },
14613
14614         /**
14615          * Returns the measured width of the specified text
14616          * @memberOf Roo.util.TextMetrics.Instance#
14617          * @param {String} text The text to measure
14618          * @return {Number} width The width in pixels
14619          */
14620         getWidth : function(text){
14621             ml.dom.style.width = 'auto';
14622             return this.getSize(text).width;
14623         },
14624
14625         /**
14626          * Returns the measured height of the specified text.  For multiline text, be sure to call
14627          * {@link #setFixedWidth} if necessary.
14628          * @memberOf Roo.util.TextMetrics.Instance#
14629          * @param {String} text The text to measure
14630          * @return {Number} height The height in pixels
14631          */
14632         getHeight : function(text){
14633             return this.getSize(text).height;
14634         }
14635     };
14636
14637     instance.bind(bindTo);
14638
14639     return instance;
14640 };
14641
14642 // backwards compat
14643 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14644  * Based on:
14645  * Ext JS Library 1.1.1
14646  * Copyright(c) 2006-2007, Ext JS, LLC.
14647  *
14648  * Originally Released Under LGPL - original licence link has changed is not relivant.
14649  *
14650  * Fork - LGPL
14651  * <script type="text/javascript">
14652  */
14653
14654 /**
14655  * @class Roo.state.Provider
14656  * Abstract base class for state provider implementations. This class provides methods
14657  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14658  * Provider interface.
14659  */
14660 Roo.state.Provider = function(){
14661     /**
14662      * @event statechange
14663      * Fires when a state change occurs.
14664      * @param {Provider} this This state provider
14665      * @param {String} key The state key which was changed
14666      * @param {String} value The encoded value for the state
14667      */
14668     this.addEvents({
14669         "statechange": true
14670     });
14671     this.state = {};
14672     Roo.state.Provider.superclass.constructor.call(this);
14673 };
14674 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14675     /**
14676      * Returns the current value for a key
14677      * @param {String} name The key name
14678      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14679      * @return {Mixed} The state data
14680      */
14681     get : function(name, defaultValue){
14682         return typeof this.state[name] == "undefined" ?
14683             defaultValue : this.state[name];
14684     },
14685     
14686     /**
14687      * Clears a value from the state
14688      * @param {String} name The key name
14689      */
14690     clear : function(name){
14691         delete this.state[name];
14692         this.fireEvent("statechange", this, name, null);
14693     },
14694     
14695     /**
14696      * Sets the value for a key
14697      * @param {String} name The key name
14698      * @param {Mixed} value The value to set
14699      */
14700     set : function(name, value){
14701         this.state[name] = value;
14702         this.fireEvent("statechange", this, name, value);
14703     },
14704     
14705     /**
14706      * Decodes a string previously encoded with {@link #encodeValue}.
14707      * @param {String} value The value to decode
14708      * @return {Mixed} The decoded value
14709      */
14710     decodeValue : function(cookie){
14711         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14712         var matches = re.exec(unescape(cookie));
14713         if(!matches || !matches[1]) return; // non state cookie
14714         var type = matches[1];
14715         var v = matches[2];
14716         switch(type){
14717             case "n":
14718                 return parseFloat(v);
14719             case "d":
14720                 return new Date(Date.parse(v));
14721             case "b":
14722                 return (v == "1");
14723             case "a":
14724                 var all = [];
14725                 var values = v.split("^");
14726                 for(var i = 0, len = values.length; i < len; i++){
14727                     all.push(this.decodeValue(values[i]));
14728                 }
14729                 return all;
14730            case "o":
14731                 var all = {};
14732                 var values = v.split("^");
14733                 for(var i = 0, len = values.length; i < len; i++){
14734                     var kv = values[i].split("=");
14735                     all[kv[0]] = this.decodeValue(kv[1]);
14736                 }
14737                 return all;
14738            default:
14739                 return v;
14740         }
14741     },
14742     
14743     /**
14744      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14745      * @param {Mixed} value The value to encode
14746      * @return {String} The encoded value
14747      */
14748     encodeValue : function(v){
14749         var enc;
14750         if(typeof v == "number"){
14751             enc = "n:" + v;
14752         }else if(typeof v == "boolean"){
14753             enc = "b:" + (v ? "1" : "0");
14754         }else if(v instanceof Date){
14755             enc = "d:" + v.toGMTString();
14756         }else if(v instanceof Array){
14757             var flat = "";
14758             for(var i = 0, len = v.length; i < len; i++){
14759                 flat += this.encodeValue(v[i]);
14760                 if(i != len-1) flat += "^";
14761             }
14762             enc = "a:" + flat;
14763         }else if(typeof v == "object"){
14764             var flat = "";
14765             for(var key in v){
14766                 if(typeof v[key] != "function"){
14767                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14768                 }
14769             }
14770             enc = "o:" + flat.substring(0, flat.length-1);
14771         }else{
14772             enc = "s:" + v;
14773         }
14774         return escape(enc);        
14775     }
14776 });
14777
14778 /*
14779  * Based on:
14780  * Ext JS Library 1.1.1
14781  * Copyright(c) 2006-2007, Ext JS, LLC.
14782  *
14783  * Originally Released Under LGPL - original licence link has changed is not relivant.
14784  *
14785  * Fork - LGPL
14786  * <script type="text/javascript">
14787  */
14788 /**
14789  * @class Roo.state.Manager
14790  * This is the global state manager. By default all components that are "state aware" check this class
14791  * for state information if you don't pass them a custom state provider. In order for this class
14792  * to be useful, it must be initialized with a provider when your application initializes.
14793  <pre><code>
14794 // in your initialization function
14795 init : function(){
14796    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14797    ...
14798    // supposed you have a {@link Roo.BorderLayout}
14799    var layout = new Roo.BorderLayout(...);
14800    layout.restoreState();
14801    // or a {Roo.BasicDialog}
14802    var dialog = new Roo.BasicDialog(...);
14803    dialog.restoreState();
14804  </code></pre>
14805  * @singleton
14806  */
14807 Roo.state.Manager = function(){
14808     var provider = new Roo.state.Provider();
14809     
14810     return {
14811         /**
14812          * Configures the default state provider for your application
14813          * @param {Provider} stateProvider The state provider to set
14814          */
14815         setProvider : function(stateProvider){
14816             provider = stateProvider;
14817         },
14818         
14819         /**
14820          * Returns the current value for a key
14821          * @param {String} name The key name
14822          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14823          * @return {Mixed} The state data
14824          */
14825         get : function(key, defaultValue){
14826             return provider.get(key, defaultValue);
14827         },
14828         
14829         /**
14830          * Sets the value for a key
14831          * @param {String} name The key name
14832          * @param {Mixed} value The state data
14833          */
14834          set : function(key, value){
14835             provider.set(key, value);
14836         },
14837         
14838         /**
14839          * Clears a value from the state
14840          * @param {String} name The key name
14841          */
14842         clear : function(key){
14843             provider.clear(key);
14844         },
14845         
14846         /**
14847          * Gets the currently configured state provider
14848          * @return {Provider} The state provider
14849          */
14850         getProvider : function(){
14851             return provider;
14852         }
14853     };
14854 }();
14855 /*
14856  * Based on:
14857  * Ext JS Library 1.1.1
14858  * Copyright(c) 2006-2007, Ext JS, LLC.
14859  *
14860  * Originally Released Under LGPL - original licence link has changed is not relivant.
14861  *
14862  * Fork - LGPL
14863  * <script type="text/javascript">
14864  */
14865 /**
14866  * @class Roo.state.CookieProvider
14867  * @extends Roo.state.Provider
14868  * The default Provider implementation which saves state via cookies.
14869  * <br />Usage:
14870  <pre><code>
14871    var cp = new Roo.state.CookieProvider({
14872        path: "/cgi-bin/",
14873        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14874        domain: "roojs.com"
14875    })
14876    Roo.state.Manager.setProvider(cp);
14877  </code></pre>
14878  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14879  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14880  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14881  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14882  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14883  * domain the page is running on including the 'www' like 'www.roojs.com')
14884  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14885  * @constructor
14886  * Create a new CookieProvider
14887  * @param {Object} config The configuration object
14888  */
14889 Roo.state.CookieProvider = function(config){
14890     Roo.state.CookieProvider.superclass.constructor.call(this);
14891     this.path = "/";
14892     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14893     this.domain = null;
14894     this.secure = false;
14895     Roo.apply(this, config);
14896     this.state = this.readCookies();
14897 };
14898
14899 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14900     // private
14901     set : function(name, value){
14902         if(typeof value == "undefined" || value === null){
14903             this.clear(name);
14904             return;
14905         }
14906         this.setCookie(name, value);
14907         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14908     },
14909
14910     // private
14911     clear : function(name){
14912         this.clearCookie(name);
14913         Roo.state.CookieProvider.superclass.clear.call(this, name);
14914     },
14915
14916     // private
14917     readCookies : function(){
14918         var cookies = {};
14919         var c = document.cookie + ";";
14920         var re = /\s?(.*?)=(.*?);/g;
14921         var matches;
14922         while((matches = re.exec(c)) != null){
14923             var name = matches[1];
14924             var value = matches[2];
14925             if(name && name.substring(0,3) == "ys-"){
14926                 cookies[name.substr(3)] = this.decodeValue(value);
14927             }
14928         }
14929         return cookies;
14930     },
14931
14932     // private
14933     setCookie : function(name, value){
14934         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14935            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14936            ((this.path == null) ? "" : ("; path=" + this.path)) +
14937            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14938            ((this.secure == true) ? "; secure" : "");
14939     },
14940
14941     // private
14942     clearCookie : function(name){
14943         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14944            ((this.path == null) ? "" : ("; path=" + this.path)) +
14945            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14946            ((this.secure == true) ? "; secure" : "");
14947     }
14948 });/*
14949  * Based on:
14950  * Ext JS Library 1.1.1
14951  * Copyright(c) 2006-2007, Ext JS, LLC.
14952  *
14953  * Originally Released Under LGPL - original licence link has changed is not relivant.
14954  *
14955  * Fork - LGPL
14956  * <script type="text/javascript">
14957  */
14958
14959
14960
14961 /*
14962  * These classes are derivatives of the similarly named classes in the YUI Library.
14963  * The original license:
14964  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14965  * Code licensed under the BSD License:
14966  * http://developer.yahoo.net/yui/license.txt
14967  */
14968
14969 (function() {
14970
14971 var Event=Roo.EventManager;
14972 var Dom=Roo.lib.Dom;
14973
14974 /**
14975  * @class Roo.dd.DragDrop
14976  * @extends Roo.util.Observable
14977  * Defines the interface and base operation of items that that can be
14978  * dragged or can be drop targets.  It was designed to be extended, overriding
14979  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14980  * Up to three html elements can be associated with a DragDrop instance:
14981  * <ul>
14982  * <li>linked element: the element that is passed into the constructor.
14983  * This is the element which defines the boundaries for interaction with
14984  * other DragDrop objects.</li>
14985  * <li>handle element(s): The drag operation only occurs if the element that
14986  * was clicked matches a handle element.  By default this is the linked
14987  * element, but there are times that you will want only a portion of the
14988  * linked element to initiate the drag operation, and the setHandleElId()
14989  * method provides a way to define this.</li>
14990  * <li>drag element: this represents the element that would be moved along
14991  * with the cursor during a drag operation.  By default, this is the linked
14992  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14993  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14994  * </li>
14995  * </ul>
14996  * This class should not be instantiated until the onload event to ensure that
14997  * the associated elements are available.
14998  * The following would define a DragDrop obj that would interact with any
14999  * other DragDrop obj in the "group1" group:
15000  * <pre>
15001  *  dd = new Roo.dd.DragDrop("div1", "group1");
15002  * </pre>
15003  * Since none of the event handlers have been implemented, nothing would
15004  * actually happen if you were to run the code above.  Normally you would
15005  * override this class or one of the default implementations, but you can
15006  * also override the methods you want on an instance of the class...
15007  * <pre>
15008  *  dd.onDragDrop = function(e, id) {
15009  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
15010  *  }
15011  * </pre>
15012  * @constructor
15013  * @param {String} id of the element that is linked to this instance
15014  * @param {String} sGroup the group of related DragDrop objects
15015  * @param {object} config an object containing configurable attributes
15016  *                Valid properties for DragDrop:
15017  *                    padding, isTarget, maintainOffset, primaryButtonOnly
15018  */
15019 Roo.dd.DragDrop = function(id, sGroup, config) {
15020     if (id) {
15021         this.init(id, sGroup, config);
15022     }
15023     
15024 };
15025
15026 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
15027
15028     /**
15029      * The id of the element associated with this object.  This is what we
15030      * refer to as the "linked element" because the size and position of
15031      * this element is used to determine when the drag and drop objects have
15032      * interacted.
15033      * @property id
15034      * @type String
15035      */
15036     id: null,
15037
15038     /**
15039      * Configuration attributes passed into the constructor
15040      * @property config
15041      * @type object
15042      */
15043     config: null,
15044
15045     /**
15046      * The id of the element that will be dragged.  By default this is same
15047      * as the linked element , but could be changed to another element. Ex:
15048      * Roo.dd.DDProxy
15049      * @property dragElId
15050      * @type String
15051      * @private
15052      */
15053     dragElId: null,
15054
15055     /**
15056      * the id of the element that initiates the drag operation.  By default
15057      * this is the linked element, but could be changed to be a child of this
15058      * element.  This lets us do things like only starting the drag when the
15059      * header element within the linked html element is clicked.
15060      * @property handleElId
15061      * @type String
15062      * @private
15063      */
15064     handleElId: null,
15065
15066     /**
15067      * An associative array of HTML tags that will be ignored if clicked.
15068      * @property invalidHandleTypes
15069      * @type {string: string}
15070      */
15071     invalidHandleTypes: null,
15072
15073     /**
15074      * An associative array of ids for elements that will be ignored if clicked
15075      * @property invalidHandleIds
15076      * @type {string: string}
15077      */
15078     invalidHandleIds: null,
15079
15080     /**
15081      * An indexted array of css class names for elements that will be ignored
15082      * if clicked.
15083      * @property invalidHandleClasses
15084      * @type string[]
15085      */
15086     invalidHandleClasses: null,
15087
15088     /**
15089      * The linked element's absolute X position at the time the drag was
15090      * started
15091      * @property startPageX
15092      * @type int
15093      * @private
15094      */
15095     startPageX: 0,
15096
15097     /**
15098      * The linked element's absolute X position at the time the drag was
15099      * started
15100      * @property startPageY
15101      * @type int
15102      * @private
15103      */
15104     startPageY: 0,
15105
15106     /**
15107      * The group defines a logical collection of DragDrop objects that are
15108      * related.  Instances only get events when interacting with other
15109      * DragDrop object in the same group.  This lets us define multiple
15110      * groups using a single DragDrop subclass if we want.
15111      * @property groups
15112      * @type {string: string}
15113      */
15114     groups: null,
15115
15116     /**
15117      * Individual drag/drop instances can be locked.  This will prevent
15118      * onmousedown start drag.
15119      * @property locked
15120      * @type boolean
15121      * @private
15122      */
15123     locked: false,
15124
15125     /**
15126      * Lock this instance
15127      * @method lock
15128      */
15129     lock: function() { this.locked = true; },
15130
15131     /**
15132      * Unlock this instace
15133      * @method unlock
15134      */
15135     unlock: function() { this.locked = false; },
15136
15137     /**
15138      * By default, all insances can be a drop target.  This can be disabled by
15139      * setting isTarget to false.
15140      * @method isTarget
15141      * @type boolean
15142      */
15143     isTarget: true,
15144
15145     /**
15146      * The padding configured for this drag and drop object for calculating
15147      * the drop zone intersection with this object.
15148      * @method padding
15149      * @type int[]
15150      */
15151     padding: null,
15152
15153     /**
15154      * Cached reference to the linked element
15155      * @property _domRef
15156      * @private
15157      */
15158     _domRef: null,
15159
15160     /**
15161      * Internal typeof flag
15162      * @property __ygDragDrop
15163      * @private
15164      */
15165     __ygDragDrop: true,
15166
15167     /**
15168      * Set to true when horizontal contraints are applied
15169      * @property constrainX
15170      * @type boolean
15171      * @private
15172      */
15173     constrainX: false,
15174
15175     /**
15176      * Set to true when vertical contraints are applied
15177      * @property constrainY
15178      * @type boolean
15179      * @private
15180      */
15181     constrainY: false,
15182
15183     /**
15184      * The left constraint
15185      * @property minX
15186      * @type int
15187      * @private
15188      */
15189     minX: 0,
15190
15191     /**
15192      * The right constraint
15193      * @property maxX
15194      * @type int
15195      * @private
15196      */
15197     maxX: 0,
15198
15199     /**
15200      * The up constraint
15201      * @property minY
15202      * @type int
15203      * @type int
15204      * @private
15205      */
15206     minY: 0,
15207
15208     /**
15209      * The down constraint
15210      * @property maxY
15211      * @type int
15212      * @private
15213      */
15214     maxY: 0,
15215
15216     /**
15217      * Maintain offsets when we resetconstraints.  Set to true when you want
15218      * the position of the element relative to its parent to stay the same
15219      * when the page changes
15220      *
15221      * @property maintainOffset
15222      * @type boolean
15223      */
15224     maintainOffset: false,
15225
15226     /**
15227      * Array of pixel locations the element will snap to if we specified a
15228      * horizontal graduation/interval.  This array is generated automatically
15229      * when you define a tick interval.
15230      * @property xTicks
15231      * @type int[]
15232      */
15233     xTicks: null,
15234
15235     /**
15236      * Array of pixel locations the element will snap to if we specified a
15237      * vertical graduation/interval.  This array is generated automatically
15238      * when you define a tick interval.
15239      * @property yTicks
15240      * @type int[]
15241      */
15242     yTicks: null,
15243
15244     /**
15245      * By default the drag and drop instance will only respond to the primary
15246      * button click (left button for a right-handed mouse).  Set to true to
15247      * allow drag and drop to start with any mouse click that is propogated
15248      * by the browser
15249      * @property primaryButtonOnly
15250      * @type boolean
15251      */
15252     primaryButtonOnly: true,
15253
15254     /**
15255      * The availabe property is false until the linked dom element is accessible.
15256      * @property available
15257      * @type boolean
15258      */
15259     available: false,
15260
15261     /**
15262      * By default, drags can only be initiated if the mousedown occurs in the
15263      * region the linked element is.  This is done in part to work around a
15264      * bug in some browsers that mis-report the mousedown if the previous
15265      * mouseup happened outside of the window.  This property is set to true
15266      * if outer handles are defined.
15267      *
15268      * @property hasOuterHandles
15269      * @type boolean
15270      * @default false
15271      */
15272     hasOuterHandles: false,
15273
15274     /**
15275      * Code that executes immediately before the startDrag event
15276      * @method b4StartDrag
15277      * @private
15278      */
15279     b4StartDrag: function(x, y) { },
15280
15281     /**
15282      * Abstract method called after a drag/drop object is clicked
15283      * and the drag or mousedown time thresholds have beeen met.
15284      * @method startDrag
15285      * @param {int} X click location
15286      * @param {int} Y click location
15287      */
15288     startDrag: function(x, y) { /* override this */ },
15289
15290     /**
15291      * Code that executes immediately before the onDrag event
15292      * @method b4Drag
15293      * @private
15294      */
15295     b4Drag: function(e) { },
15296
15297     /**
15298      * Abstract method called during the onMouseMove event while dragging an
15299      * object.
15300      * @method onDrag
15301      * @param {Event} e the mousemove event
15302      */
15303     onDrag: function(e) { /* override this */ },
15304
15305     /**
15306      * Abstract method called when this element fist begins hovering over
15307      * another DragDrop obj
15308      * @method onDragEnter
15309      * @param {Event} e the mousemove event
15310      * @param {String|DragDrop[]} id In POINT mode, the element
15311      * id this is hovering over.  In INTERSECT mode, an array of one or more
15312      * dragdrop items being hovered over.
15313      */
15314     onDragEnter: function(e, id) { /* override this */ },
15315
15316     /**
15317      * Code that executes immediately before the onDragOver event
15318      * @method b4DragOver
15319      * @private
15320      */
15321     b4DragOver: function(e) { },
15322
15323     /**
15324      * Abstract method called when this element is hovering over another
15325      * DragDrop obj
15326      * @method onDragOver
15327      * @param {Event} e the mousemove event
15328      * @param {String|DragDrop[]} id In POINT mode, the element
15329      * id this is hovering over.  In INTERSECT mode, an array of dd items
15330      * being hovered over.
15331      */
15332     onDragOver: function(e, id) { /* override this */ },
15333
15334     /**
15335      * Code that executes immediately before the onDragOut event
15336      * @method b4DragOut
15337      * @private
15338      */
15339     b4DragOut: function(e) { },
15340
15341     /**
15342      * Abstract method called when we are no longer hovering over an element
15343      * @method onDragOut
15344      * @param {Event} e the mousemove event
15345      * @param {String|DragDrop[]} id In POINT mode, the element
15346      * id this was hovering over.  In INTERSECT mode, an array of dd items
15347      * that the mouse is no longer over.
15348      */
15349     onDragOut: function(e, id) { /* override this */ },
15350
15351     /**
15352      * Code that executes immediately before the onDragDrop event
15353      * @method b4DragDrop
15354      * @private
15355      */
15356     b4DragDrop: function(e) { },
15357
15358     /**
15359      * Abstract method called when this item is dropped on another DragDrop
15360      * obj
15361      * @method onDragDrop
15362      * @param {Event} e the mouseup event
15363      * @param {String|DragDrop[]} id In POINT mode, the element
15364      * id this was dropped on.  In INTERSECT mode, an array of dd items this
15365      * was dropped on.
15366      */
15367     onDragDrop: function(e, id) { /* override this */ },
15368
15369     /**
15370      * Abstract method called when this item is dropped on an area with no
15371      * drop target
15372      * @method onInvalidDrop
15373      * @param {Event} e the mouseup event
15374      */
15375     onInvalidDrop: function(e) { /* override this */ },
15376
15377     /**
15378      * Code that executes immediately before the endDrag event
15379      * @method b4EndDrag
15380      * @private
15381      */
15382     b4EndDrag: function(e) { },
15383
15384     /**
15385      * Fired when we are done dragging the object
15386      * @method endDrag
15387      * @param {Event} e the mouseup event
15388      */
15389     endDrag: function(e) { /* override this */ },
15390
15391     /**
15392      * Code executed immediately before the onMouseDown event
15393      * @method b4MouseDown
15394      * @param {Event} e the mousedown event
15395      * @private
15396      */
15397     b4MouseDown: function(e) {  },
15398
15399     /**
15400      * Event handler that fires when a drag/drop obj gets a mousedown
15401      * @method onMouseDown
15402      * @param {Event} e the mousedown event
15403      */
15404     onMouseDown: function(e) { /* override this */ },
15405
15406     /**
15407      * Event handler that fires when a drag/drop obj gets a mouseup
15408      * @method onMouseUp
15409      * @param {Event} e the mouseup event
15410      */
15411     onMouseUp: function(e) { /* override this */ },
15412
15413     /**
15414      * Override the onAvailable method to do what is needed after the initial
15415      * position was determined.
15416      * @method onAvailable
15417      */
15418     onAvailable: function () {
15419     },
15420
15421     /*
15422      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
15423      * @type Object
15424      */
15425     defaultPadding : {left:0, right:0, top:0, bottom:0},
15426
15427     /*
15428      * Initializes the drag drop object's constraints to restrict movement to a certain element.
15429  *
15430  * Usage:
15431  <pre><code>
15432  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
15433                 { dragElId: "existingProxyDiv" });
15434  dd.startDrag = function(){
15435      this.constrainTo("parent-id");
15436  };
15437  </code></pre>
15438  * Or you can initalize it using the {@link Roo.Element} object:
15439  <pre><code>
15440  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
15441      startDrag : function(){
15442          this.constrainTo("parent-id");
15443      }
15444  });
15445  </code></pre>
15446      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
15447      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
15448      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
15449      * an object containing the sides to pad. For example: {right:10, bottom:10}
15450      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
15451      */
15452     constrainTo : function(constrainTo, pad, inContent){
15453         if(typeof pad == "number"){
15454             pad = {left: pad, right:pad, top:pad, bottom:pad};
15455         }
15456         pad = pad || this.defaultPadding;
15457         var b = Roo.get(this.getEl()).getBox();
15458         var ce = Roo.get(constrainTo);
15459         var s = ce.getScroll();
15460         var c, cd = ce.dom;
15461         if(cd == document.body){
15462             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15463         }else{
15464             xy = ce.getXY();
15465             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15466         }
15467
15468
15469         var topSpace = b.y - c.y;
15470         var leftSpace = b.x - c.x;
15471
15472         this.resetConstraints();
15473         this.setXConstraint(leftSpace - (pad.left||0), // left
15474                 c.width - leftSpace - b.width - (pad.right||0) //right
15475         );
15476         this.setYConstraint(topSpace - (pad.top||0), //top
15477                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15478         );
15479     },
15480
15481     /**
15482      * Returns a reference to the linked element
15483      * @method getEl
15484      * @return {HTMLElement} the html element
15485      */
15486     getEl: function() {
15487         if (!this._domRef) {
15488             this._domRef = Roo.getDom(this.id);
15489         }
15490
15491         return this._domRef;
15492     },
15493
15494     /**
15495      * Returns a reference to the actual element to drag.  By default this is
15496      * the same as the html element, but it can be assigned to another
15497      * element. An example of this can be found in Roo.dd.DDProxy
15498      * @method getDragEl
15499      * @return {HTMLElement} the html element
15500      */
15501     getDragEl: function() {
15502         return Roo.getDom(this.dragElId);
15503     },
15504
15505     /**
15506      * Sets up the DragDrop object.  Must be called in the constructor of any
15507      * Roo.dd.DragDrop subclass
15508      * @method init
15509      * @param id the id of the linked element
15510      * @param {String} sGroup the group of related items
15511      * @param {object} config configuration attributes
15512      */
15513     init: function(id, sGroup, config) {
15514         this.initTarget(id, sGroup, config);
15515         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15516         // Event.on(this.id, "selectstart", Event.preventDefault);
15517     },
15518
15519     /**
15520      * Initializes Targeting functionality only... the object does not
15521      * get a mousedown handler.
15522      * @method initTarget
15523      * @param id the id of the linked element
15524      * @param {String} sGroup the group of related items
15525      * @param {object} config configuration attributes
15526      */
15527     initTarget: function(id, sGroup, config) {
15528
15529         // configuration attributes
15530         this.config = config || {};
15531
15532         // create a local reference to the drag and drop manager
15533         this.DDM = Roo.dd.DDM;
15534         // initialize the groups array
15535         this.groups = {};
15536
15537         // assume that we have an element reference instead of an id if the
15538         // parameter is not a string
15539         if (typeof id !== "string") {
15540             id = Roo.id(id);
15541         }
15542
15543         // set the id
15544         this.id = id;
15545
15546         // add to an interaction group
15547         this.addToGroup((sGroup) ? sGroup : "default");
15548
15549         // We don't want to register this as the handle with the manager
15550         // so we just set the id rather than calling the setter.
15551         this.handleElId = id;
15552
15553         // the linked element is the element that gets dragged by default
15554         this.setDragElId(id);
15555
15556         // by default, clicked anchors will not start drag operations.
15557         this.invalidHandleTypes = { A: "A" };
15558         this.invalidHandleIds = {};
15559         this.invalidHandleClasses = [];
15560
15561         this.applyConfig();
15562
15563         this.handleOnAvailable();
15564     },
15565
15566     /**
15567      * Applies the configuration parameters that were passed into the constructor.
15568      * This is supposed to happen at each level through the inheritance chain.  So
15569      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15570      * DragDrop in order to get all of the parameters that are available in
15571      * each object.
15572      * @method applyConfig
15573      */
15574     applyConfig: function() {
15575
15576         // configurable properties:
15577         //    padding, isTarget, maintainOffset, primaryButtonOnly
15578         this.padding           = this.config.padding || [0, 0, 0, 0];
15579         this.isTarget          = (this.config.isTarget !== false);
15580         this.maintainOffset    = (this.config.maintainOffset);
15581         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15582
15583     },
15584
15585     /**
15586      * Executed when the linked element is available
15587      * @method handleOnAvailable
15588      * @private
15589      */
15590     handleOnAvailable: function() {
15591         this.available = true;
15592         this.resetConstraints();
15593         this.onAvailable();
15594     },
15595
15596      /**
15597      * Configures the padding for the target zone in px.  Effectively expands
15598      * (or reduces) the virtual object size for targeting calculations.
15599      * Supports css-style shorthand; if only one parameter is passed, all sides
15600      * will have that padding, and if only two are passed, the top and bottom
15601      * will have the first param, the left and right the second.
15602      * @method setPadding
15603      * @param {int} iTop    Top pad
15604      * @param {int} iRight  Right pad
15605      * @param {int} iBot    Bot pad
15606      * @param {int} iLeft   Left pad
15607      */
15608     setPadding: function(iTop, iRight, iBot, iLeft) {
15609         // this.padding = [iLeft, iRight, iTop, iBot];
15610         if (!iRight && 0 !== iRight) {
15611             this.padding = [iTop, iTop, iTop, iTop];
15612         } else if (!iBot && 0 !== iBot) {
15613             this.padding = [iTop, iRight, iTop, iRight];
15614         } else {
15615             this.padding = [iTop, iRight, iBot, iLeft];
15616         }
15617     },
15618
15619     /**
15620      * Stores the initial placement of the linked element.
15621      * @method setInitialPosition
15622      * @param {int} diffX   the X offset, default 0
15623      * @param {int} diffY   the Y offset, default 0
15624      */
15625     setInitPosition: function(diffX, diffY) {
15626         var el = this.getEl();
15627
15628         if (!this.DDM.verifyEl(el)) {
15629             return;
15630         }
15631
15632         var dx = diffX || 0;
15633         var dy = diffY || 0;
15634
15635         var p = Dom.getXY( el );
15636
15637         this.initPageX = p[0] - dx;
15638         this.initPageY = p[1] - dy;
15639
15640         this.lastPageX = p[0];
15641         this.lastPageY = p[1];
15642
15643
15644         this.setStartPosition(p);
15645     },
15646
15647     /**
15648      * Sets the start position of the element.  This is set when the obj
15649      * is initialized, the reset when a drag is started.
15650      * @method setStartPosition
15651      * @param pos current position (from previous lookup)
15652      * @private
15653      */
15654     setStartPosition: function(pos) {
15655         var p = pos || Dom.getXY( this.getEl() );
15656         this.deltaSetXY = null;
15657
15658         this.startPageX = p[0];
15659         this.startPageY = p[1];
15660     },
15661
15662     /**
15663      * Add this instance to a group of related drag/drop objects.  All
15664      * instances belong to at least one group, and can belong to as many
15665      * groups as needed.
15666      * @method addToGroup
15667      * @param sGroup {string} the name of the group
15668      */
15669     addToGroup: function(sGroup) {
15670         this.groups[sGroup] = true;
15671         this.DDM.regDragDrop(this, sGroup);
15672     },
15673
15674     /**
15675      * Remove's this instance from the supplied interaction group
15676      * @method removeFromGroup
15677      * @param {string}  sGroup  The group to drop
15678      */
15679     removeFromGroup: function(sGroup) {
15680         if (this.groups[sGroup]) {
15681             delete this.groups[sGroup];
15682         }
15683
15684         this.DDM.removeDDFromGroup(this, sGroup);
15685     },
15686
15687     /**
15688      * Allows you to specify that an element other than the linked element
15689      * will be moved with the cursor during a drag
15690      * @method setDragElId
15691      * @param id {string} the id of the element that will be used to initiate the drag
15692      */
15693     setDragElId: function(id) {
15694         this.dragElId = id;
15695     },
15696
15697     /**
15698      * Allows you to specify a child of the linked element that should be
15699      * used to initiate the drag operation.  An example of this would be if
15700      * you have a content div with text and links.  Clicking anywhere in the
15701      * content area would normally start the drag operation.  Use this method
15702      * to specify that an element inside of the content div is the element
15703      * that starts the drag operation.
15704      * @method setHandleElId
15705      * @param id {string} the id of the element that will be used to
15706      * initiate the drag.
15707      */
15708     setHandleElId: function(id) {
15709         if (typeof id !== "string") {
15710             id = Roo.id(id);
15711         }
15712         this.handleElId = id;
15713         this.DDM.regHandle(this.id, id);
15714     },
15715
15716     /**
15717      * Allows you to set an element outside of the linked element as a drag
15718      * handle
15719      * @method setOuterHandleElId
15720      * @param id the id of the element that will be used to initiate the drag
15721      */
15722     setOuterHandleElId: function(id) {
15723         if (typeof id !== "string") {
15724             id = Roo.id(id);
15725         }
15726         Event.on(id, "mousedown",
15727                 this.handleMouseDown, this);
15728         this.setHandleElId(id);
15729
15730         this.hasOuterHandles = true;
15731     },
15732
15733     /**
15734      * Remove all drag and drop hooks for this element
15735      * @method unreg
15736      */
15737     unreg: function() {
15738         Event.un(this.id, "mousedown",
15739                 this.handleMouseDown);
15740         this._domRef = null;
15741         this.DDM._remove(this);
15742     },
15743
15744     destroy : function(){
15745         this.unreg();
15746     },
15747
15748     /**
15749      * Returns true if this instance is locked, or the drag drop mgr is locked
15750      * (meaning that all drag/drop is disabled on the page.)
15751      * @method isLocked
15752      * @return {boolean} true if this obj or all drag/drop is locked, else
15753      * false
15754      */
15755     isLocked: function() {
15756         return (this.DDM.isLocked() || this.locked);
15757     },
15758
15759     /**
15760      * Fired when this object is clicked
15761      * @method handleMouseDown
15762      * @param {Event} e
15763      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15764      * @private
15765      */
15766     handleMouseDown: function(e, oDD){
15767         if (this.primaryButtonOnly && e.button != 0) {
15768             return;
15769         }
15770
15771         if (this.isLocked()) {
15772             return;
15773         }
15774
15775         this.DDM.refreshCache(this.groups);
15776
15777         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15778         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15779         } else {
15780             if (this.clickValidator(e)) {
15781
15782                 // set the initial element position
15783                 this.setStartPosition();
15784
15785
15786                 this.b4MouseDown(e);
15787                 this.onMouseDown(e);
15788
15789                 this.DDM.handleMouseDown(e, this);
15790
15791                 this.DDM.stopEvent(e);
15792             } else {
15793
15794
15795             }
15796         }
15797     },
15798
15799     clickValidator: function(e) {
15800         var target = e.getTarget();
15801         return ( this.isValidHandleChild(target) &&
15802                     (this.id == this.handleElId ||
15803                         this.DDM.handleWasClicked(target, this.id)) );
15804     },
15805
15806     /**
15807      * Allows you to specify a tag name that should not start a drag operation
15808      * when clicked.  This is designed to facilitate embedding links within a
15809      * drag handle that do something other than start the drag.
15810      * @method addInvalidHandleType
15811      * @param {string} tagName the type of element to exclude
15812      */
15813     addInvalidHandleType: function(tagName) {
15814         var type = tagName.toUpperCase();
15815         this.invalidHandleTypes[type] = type;
15816     },
15817
15818     /**
15819      * Lets you to specify an element id for a child of a drag handle
15820      * that should not initiate a drag
15821      * @method addInvalidHandleId
15822      * @param {string} id the element id of the element you wish to ignore
15823      */
15824     addInvalidHandleId: function(id) {
15825         if (typeof id !== "string") {
15826             id = Roo.id(id);
15827         }
15828         this.invalidHandleIds[id] = id;
15829     },
15830
15831     /**
15832      * Lets you specify a css class of elements that will not initiate a drag
15833      * @method addInvalidHandleClass
15834      * @param {string} cssClass the class of the elements you wish to ignore
15835      */
15836     addInvalidHandleClass: function(cssClass) {
15837         this.invalidHandleClasses.push(cssClass);
15838     },
15839
15840     /**
15841      * Unsets an excluded tag name set by addInvalidHandleType
15842      * @method removeInvalidHandleType
15843      * @param {string} tagName the type of element to unexclude
15844      */
15845     removeInvalidHandleType: function(tagName) {
15846         var type = tagName.toUpperCase();
15847         // this.invalidHandleTypes[type] = null;
15848         delete this.invalidHandleTypes[type];
15849     },
15850
15851     /**
15852      * Unsets an invalid handle id
15853      * @method removeInvalidHandleId
15854      * @param {string} id the id of the element to re-enable
15855      */
15856     removeInvalidHandleId: function(id) {
15857         if (typeof id !== "string") {
15858             id = Roo.id(id);
15859         }
15860         delete this.invalidHandleIds[id];
15861     },
15862
15863     /**
15864      * Unsets an invalid css class
15865      * @method removeInvalidHandleClass
15866      * @param {string} cssClass the class of the element(s) you wish to
15867      * re-enable
15868      */
15869     removeInvalidHandleClass: function(cssClass) {
15870         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15871             if (this.invalidHandleClasses[i] == cssClass) {
15872                 delete this.invalidHandleClasses[i];
15873             }
15874         }
15875     },
15876
15877     /**
15878      * Checks the tag exclusion list to see if this click should be ignored
15879      * @method isValidHandleChild
15880      * @param {HTMLElement} node the HTMLElement to evaluate
15881      * @return {boolean} true if this is a valid tag type, false if not
15882      */
15883     isValidHandleChild: function(node) {
15884
15885         var valid = true;
15886         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15887         var nodeName;
15888         try {
15889             nodeName = node.nodeName.toUpperCase();
15890         } catch(e) {
15891             nodeName = node.nodeName;
15892         }
15893         valid = valid && !this.invalidHandleTypes[nodeName];
15894         valid = valid && !this.invalidHandleIds[node.id];
15895
15896         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15897             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15898         }
15899
15900
15901         return valid;
15902
15903     },
15904
15905     /**
15906      * Create the array of horizontal tick marks if an interval was specified
15907      * in setXConstraint().
15908      * @method setXTicks
15909      * @private
15910      */
15911     setXTicks: function(iStartX, iTickSize) {
15912         this.xTicks = [];
15913         this.xTickSize = iTickSize;
15914
15915         var tickMap = {};
15916
15917         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15918             if (!tickMap[i]) {
15919                 this.xTicks[this.xTicks.length] = i;
15920                 tickMap[i] = true;
15921             }
15922         }
15923
15924         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15925             if (!tickMap[i]) {
15926                 this.xTicks[this.xTicks.length] = i;
15927                 tickMap[i] = true;
15928             }
15929         }
15930
15931         this.xTicks.sort(this.DDM.numericSort) ;
15932     },
15933
15934     /**
15935      * Create the array of vertical tick marks if an interval was specified in
15936      * setYConstraint().
15937      * @method setYTicks
15938      * @private
15939      */
15940     setYTicks: function(iStartY, iTickSize) {
15941         this.yTicks = [];
15942         this.yTickSize = iTickSize;
15943
15944         var tickMap = {};
15945
15946         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15947             if (!tickMap[i]) {
15948                 this.yTicks[this.yTicks.length] = i;
15949                 tickMap[i] = true;
15950             }
15951         }
15952
15953         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15954             if (!tickMap[i]) {
15955                 this.yTicks[this.yTicks.length] = i;
15956                 tickMap[i] = true;
15957             }
15958         }
15959
15960         this.yTicks.sort(this.DDM.numericSort) ;
15961     },
15962
15963     /**
15964      * By default, the element can be dragged any place on the screen.  Use
15965      * this method to limit the horizontal travel of the element.  Pass in
15966      * 0,0 for the parameters if you want to lock the drag to the y axis.
15967      * @method setXConstraint
15968      * @param {int} iLeft the number of pixels the element can move to the left
15969      * @param {int} iRight the number of pixels the element can move to the
15970      * right
15971      * @param {int} iTickSize optional parameter for specifying that the
15972      * element
15973      * should move iTickSize pixels at a time.
15974      */
15975     setXConstraint: function(iLeft, iRight, iTickSize) {
15976         this.leftConstraint = iLeft;
15977         this.rightConstraint = iRight;
15978
15979         this.minX = this.initPageX - iLeft;
15980         this.maxX = this.initPageX + iRight;
15981         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15982
15983         this.constrainX = true;
15984     },
15985
15986     /**
15987      * Clears any constraints applied to this instance.  Also clears ticks
15988      * since they can't exist independent of a constraint at this time.
15989      * @method clearConstraints
15990      */
15991     clearConstraints: function() {
15992         this.constrainX = false;
15993         this.constrainY = false;
15994         this.clearTicks();
15995     },
15996
15997     /**
15998      * Clears any tick interval defined for this instance
15999      * @method clearTicks
16000      */
16001     clearTicks: function() {
16002         this.xTicks = null;
16003         this.yTicks = null;
16004         this.xTickSize = 0;
16005         this.yTickSize = 0;
16006     },
16007
16008     /**
16009      * By default, the element can be dragged any place on the screen.  Set
16010      * this to limit the vertical travel of the element.  Pass in 0,0 for the
16011      * parameters if you want to lock the drag to the x axis.
16012      * @method setYConstraint
16013      * @param {int} iUp the number of pixels the element can move up
16014      * @param {int} iDown the number of pixels the element can move down
16015      * @param {int} iTickSize optional parameter for specifying that the
16016      * element should move iTickSize pixels at a time.
16017      */
16018     setYConstraint: function(iUp, iDown, iTickSize) {
16019         this.topConstraint = iUp;
16020         this.bottomConstraint = iDown;
16021
16022         this.minY = this.initPageY - iUp;
16023         this.maxY = this.initPageY + iDown;
16024         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
16025
16026         this.constrainY = true;
16027
16028     },
16029
16030     /**
16031      * resetConstraints must be called if you manually reposition a dd element.
16032      * @method resetConstraints
16033      * @param {boolean} maintainOffset
16034      */
16035     resetConstraints: function() {
16036
16037
16038         // Maintain offsets if necessary
16039         if (this.initPageX || this.initPageX === 0) {
16040             // figure out how much this thing has moved
16041             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
16042             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
16043
16044             this.setInitPosition(dx, dy);
16045
16046         // This is the first time we have detected the element's position
16047         } else {
16048             this.setInitPosition();
16049         }
16050
16051         if (this.constrainX) {
16052             this.setXConstraint( this.leftConstraint,
16053                                  this.rightConstraint,
16054                                  this.xTickSize        );
16055         }
16056
16057         if (this.constrainY) {
16058             this.setYConstraint( this.topConstraint,
16059                                  this.bottomConstraint,
16060                                  this.yTickSize         );
16061         }
16062     },
16063
16064     /**
16065      * Normally the drag element is moved pixel by pixel, but we can specify
16066      * that it move a number of pixels at a time.  This method resolves the
16067      * location when we have it set up like this.
16068      * @method getTick
16069      * @param {int} val where we want to place the object
16070      * @param {int[]} tickArray sorted array of valid points
16071      * @return {int} the closest tick
16072      * @private
16073      */
16074     getTick: function(val, tickArray) {
16075
16076         if (!tickArray) {
16077             // If tick interval is not defined, it is effectively 1 pixel,
16078             // so we return the value passed to us.
16079             return val;
16080         } else if (tickArray[0] >= val) {
16081             // The value is lower than the first tick, so we return the first
16082             // tick.
16083             return tickArray[0];
16084         } else {
16085             for (var i=0, len=tickArray.length; i<len; ++i) {
16086                 var next = i + 1;
16087                 if (tickArray[next] && tickArray[next] >= val) {
16088                     var diff1 = val - tickArray[i];
16089                     var diff2 = tickArray[next] - val;
16090                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
16091                 }
16092             }
16093
16094             // The value is larger than the last tick, so we return the last
16095             // tick.
16096             return tickArray[tickArray.length - 1];
16097         }
16098     },
16099
16100     /**
16101      * toString method
16102      * @method toString
16103      * @return {string} string representation of the dd obj
16104      */
16105     toString: function() {
16106         return ("DragDrop " + this.id);
16107     }
16108
16109 });
16110
16111 })();
16112 /*
16113  * Based on:
16114  * Ext JS Library 1.1.1
16115  * Copyright(c) 2006-2007, Ext JS, LLC.
16116  *
16117  * Originally Released Under LGPL - original licence link has changed is not relivant.
16118  *
16119  * Fork - LGPL
16120  * <script type="text/javascript">
16121  */
16122
16123
16124 /**
16125  * The drag and drop utility provides a framework for building drag and drop
16126  * applications.  In addition to enabling drag and drop for specific elements,
16127  * the drag and drop elements are tracked by the manager class, and the
16128  * interactions between the various elements are tracked during the drag and
16129  * the implementing code is notified about these important moments.
16130  */
16131
16132 // Only load the library once.  Rewriting the manager class would orphan
16133 // existing drag and drop instances.
16134 if (!Roo.dd.DragDropMgr) {
16135
16136 /**
16137  * @class Roo.dd.DragDropMgr
16138  * DragDropMgr is a singleton that tracks the element interaction for
16139  * all DragDrop items in the window.  Generally, you will not call
16140  * this class directly, but it does have helper methods that could
16141  * be useful in your DragDrop implementations.
16142  * @singleton
16143  */
16144 Roo.dd.DragDropMgr = function() {
16145
16146     var Event = Roo.EventManager;
16147
16148     return {
16149
16150         /**
16151          * Two dimensional Array of registered DragDrop objects.  The first
16152          * dimension is the DragDrop item group, the second the DragDrop
16153          * object.
16154          * @property ids
16155          * @type {string: string}
16156          * @private
16157          * @static
16158          */
16159         ids: {},
16160
16161         /**
16162          * Array of element ids defined as drag handles.  Used to determine
16163          * if the element that generated the mousedown event is actually the
16164          * handle and not the html element itself.
16165          * @property handleIds
16166          * @type {string: string}
16167          * @private
16168          * @static
16169          */
16170         handleIds: {},
16171
16172         /**
16173          * the DragDrop object that is currently being dragged
16174          * @property dragCurrent
16175          * @type DragDrop
16176          * @private
16177          * @static
16178          **/
16179         dragCurrent: null,
16180
16181         /**
16182          * the DragDrop object(s) that are being hovered over
16183          * @property dragOvers
16184          * @type Array
16185          * @private
16186          * @static
16187          */
16188         dragOvers: {},
16189
16190         /**
16191          * the X distance between the cursor and the object being dragged
16192          * @property deltaX
16193          * @type int
16194          * @private
16195          * @static
16196          */
16197         deltaX: 0,
16198
16199         /**
16200          * the Y distance between the cursor and the object being dragged
16201          * @property deltaY
16202          * @type int
16203          * @private
16204          * @static
16205          */
16206         deltaY: 0,
16207
16208         /**
16209          * Flag to determine if we should prevent the default behavior of the
16210          * events we define. By default this is true, but this can be set to
16211          * false if you need the default behavior (not recommended)
16212          * @property preventDefault
16213          * @type boolean
16214          * @static
16215          */
16216         preventDefault: true,
16217
16218         /**
16219          * Flag to determine if we should stop the propagation of the events
16220          * we generate. This is true by default but you may want to set it to
16221          * false if the html element contains other features that require the
16222          * mouse click.
16223          * @property stopPropagation
16224          * @type boolean
16225          * @static
16226          */
16227         stopPropagation: true,
16228
16229         /**
16230          * Internal flag that is set to true when drag and drop has been
16231          * intialized
16232          * @property initialized
16233          * @private
16234          * @static
16235          */
16236         initalized: false,
16237
16238         /**
16239          * All drag and drop can be disabled.
16240          * @property locked
16241          * @private
16242          * @static
16243          */
16244         locked: false,
16245
16246         /**
16247          * Called the first time an element is registered.
16248          * @method init
16249          * @private
16250          * @static
16251          */
16252         init: function() {
16253             this.initialized = true;
16254         },
16255
16256         /**
16257          * In point mode, drag and drop interaction is defined by the
16258          * location of the cursor during the drag/drop
16259          * @property POINT
16260          * @type int
16261          * @static
16262          */
16263         POINT: 0,
16264
16265         /**
16266          * In intersect mode, drag and drop interactio nis defined by the
16267          * overlap of two or more drag and drop objects.
16268          * @property INTERSECT
16269          * @type int
16270          * @static
16271          */
16272         INTERSECT: 1,
16273
16274         /**
16275          * The current drag and drop mode.  Default: POINT
16276          * @property mode
16277          * @type int
16278          * @static
16279          */
16280         mode: 0,
16281
16282         /**
16283          * Runs method on all drag and drop objects
16284          * @method _execOnAll
16285          * @private
16286          * @static
16287          */
16288         _execOnAll: function(sMethod, args) {
16289             for (var i in this.ids) {
16290                 for (var j in this.ids[i]) {
16291                     var oDD = this.ids[i][j];
16292                     if (! this.isTypeOfDD(oDD)) {
16293                         continue;
16294                     }
16295                     oDD[sMethod].apply(oDD, args);
16296                 }
16297             }
16298         },
16299
16300         /**
16301          * Drag and drop initialization.  Sets up the global event handlers
16302          * @method _onLoad
16303          * @private
16304          * @static
16305          */
16306         _onLoad: function() {
16307
16308             this.init();
16309
16310
16311             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
16312             Event.on(document, "mousemove", this.handleMouseMove, this, true);
16313             Event.on(window,   "unload",    this._onUnload, this, true);
16314             Event.on(window,   "resize",    this._onResize, this, true);
16315             // Event.on(window,   "mouseout",    this._test);
16316
16317         },
16318
16319         /**
16320          * Reset constraints on all drag and drop objs
16321          * @method _onResize
16322          * @private
16323          * @static
16324          */
16325         _onResize: function(e) {
16326             this._execOnAll("resetConstraints", []);
16327         },
16328
16329         /**
16330          * Lock all drag and drop functionality
16331          * @method lock
16332          * @static
16333          */
16334         lock: function() { this.locked = true; },
16335
16336         /**
16337          * Unlock all drag and drop functionality
16338          * @method unlock
16339          * @static
16340          */
16341         unlock: function() { this.locked = false; },
16342
16343         /**
16344          * Is drag and drop locked?
16345          * @method isLocked
16346          * @return {boolean} True if drag and drop is locked, false otherwise.
16347          * @static
16348          */
16349         isLocked: function() { return this.locked; },
16350
16351         /**
16352          * Location cache that is set for all drag drop objects when a drag is
16353          * initiated, cleared when the drag is finished.
16354          * @property locationCache
16355          * @private
16356          * @static
16357          */
16358         locationCache: {},
16359
16360         /**
16361          * Set useCache to false if you want to force object the lookup of each
16362          * drag and drop linked element constantly during a drag.
16363          * @property useCache
16364          * @type boolean
16365          * @static
16366          */
16367         useCache: true,
16368
16369         /**
16370          * The number of pixels that the mouse needs to move after the
16371          * mousedown before the drag is initiated.  Default=3;
16372          * @property clickPixelThresh
16373          * @type int
16374          * @static
16375          */
16376         clickPixelThresh: 3,
16377
16378         /**
16379          * The number of milliseconds after the mousedown event to initiate the
16380          * drag if we don't get a mouseup event. Default=1000
16381          * @property clickTimeThresh
16382          * @type int
16383          * @static
16384          */
16385         clickTimeThresh: 350,
16386
16387         /**
16388          * Flag that indicates that either the drag pixel threshold or the
16389          * mousdown time threshold has been met
16390          * @property dragThreshMet
16391          * @type boolean
16392          * @private
16393          * @static
16394          */
16395         dragThreshMet: false,
16396
16397         /**
16398          * Timeout used for the click time threshold
16399          * @property clickTimeout
16400          * @type Object
16401          * @private
16402          * @static
16403          */
16404         clickTimeout: null,
16405
16406         /**
16407          * The X position of the mousedown event stored for later use when a
16408          * drag threshold is met.
16409          * @property startX
16410          * @type int
16411          * @private
16412          * @static
16413          */
16414         startX: 0,
16415
16416         /**
16417          * The Y position of the mousedown event stored for later use when a
16418          * drag threshold is met.
16419          * @property startY
16420          * @type int
16421          * @private
16422          * @static
16423          */
16424         startY: 0,
16425
16426         /**
16427          * Each DragDrop instance must be registered with the DragDropMgr.
16428          * This is executed in DragDrop.init()
16429          * @method regDragDrop
16430          * @param {DragDrop} oDD the DragDrop object to register
16431          * @param {String} sGroup the name of the group this element belongs to
16432          * @static
16433          */
16434         regDragDrop: function(oDD, sGroup) {
16435             if (!this.initialized) { this.init(); }
16436
16437             if (!this.ids[sGroup]) {
16438                 this.ids[sGroup] = {};
16439             }
16440             this.ids[sGroup][oDD.id] = oDD;
16441         },
16442
16443         /**
16444          * Removes the supplied dd instance from the supplied group. Executed
16445          * by DragDrop.removeFromGroup, so don't call this function directly.
16446          * @method removeDDFromGroup
16447          * @private
16448          * @static
16449          */
16450         removeDDFromGroup: function(oDD, sGroup) {
16451             if (!this.ids[sGroup]) {
16452                 this.ids[sGroup] = {};
16453             }
16454
16455             var obj = this.ids[sGroup];
16456             if (obj && obj[oDD.id]) {
16457                 delete obj[oDD.id];
16458             }
16459         },
16460
16461         /**
16462          * Unregisters a drag and drop item.  This is executed in
16463          * DragDrop.unreg, use that method instead of calling this directly.
16464          * @method _remove
16465          * @private
16466          * @static
16467          */
16468         _remove: function(oDD) {
16469             for (var g in oDD.groups) {
16470                 if (g && this.ids[g][oDD.id]) {
16471                     delete this.ids[g][oDD.id];
16472                 }
16473             }
16474             delete this.handleIds[oDD.id];
16475         },
16476
16477         /**
16478          * Each DragDrop handle element must be registered.  This is done
16479          * automatically when executing DragDrop.setHandleElId()
16480          * @method regHandle
16481          * @param {String} sDDId the DragDrop id this element is a handle for
16482          * @param {String} sHandleId the id of the element that is the drag
16483          * handle
16484          * @static
16485          */
16486         regHandle: function(sDDId, sHandleId) {
16487             if (!this.handleIds[sDDId]) {
16488                 this.handleIds[sDDId] = {};
16489             }
16490             this.handleIds[sDDId][sHandleId] = sHandleId;
16491         },
16492
16493         /**
16494          * Utility function to determine if a given element has been
16495          * registered as a drag drop item.
16496          * @method isDragDrop
16497          * @param {String} id the element id to check
16498          * @return {boolean} true if this element is a DragDrop item,
16499          * false otherwise
16500          * @static
16501          */
16502         isDragDrop: function(id) {
16503             return ( this.getDDById(id) ) ? true : false;
16504         },
16505
16506         /**
16507          * Returns the drag and drop instances that are in all groups the
16508          * passed in instance belongs to.
16509          * @method getRelated
16510          * @param {DragDrop} p_oDD the obj to get related data for
16511          * @param {boolean} bTargetsOnly if true, only return targetable objs
16512          * @return {DragDrop[]} the related instances
16513          * @static
16514          */
16515         getRelated: function(p_oDD, bTargetsOnly) {
16516             var oDDs = [];
16517             for (var i in p_oDD.groups) {
16518                 for (j in this.ids[i]) {
16519                     var dd = this.ids[i][j];
16520                     if (! this.isTypeOfDD(dd)) {
16521                         continue;
16522                     }
16523                     if (!bTargetsOnly || dd.isTarget) {
16524                         oDDs[oDDs.length] = dd;
16525                     }
16526                 }
16527             }
16528
16529             return oDDs;
16530         },
16531
16532         /**
16533          * Returns true if the specified dd target is a legal target for
16534          * the specifice drag obj
16535          * @method isLegalTarget
16536          * @param {DragDrop} the drag obj
16537          * @param {DragDrop} the target
16538          * @return {boolean} true if the target is a legal target for the
16539          * dd obj
16540          * @static
16541          */
16542         isLegalTarget: function (oDD, oTargetDD) {
16543             var targets = this.getRelated(oDD, true);
16544             for (var i=0, len=targets.length;i<len;++i) {
16545                 if (targets[i].id == oTargetDD.id) {
16546                     return true;
16547                 }
16548             }
16549
16550             return false;
16551         },
16552
16553         /**
16554          * My goal is to be able to transparently determine if an object is
16555          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16556          * returns "object", oDD.constructor.toString() always returns
16557          * "DragDrop" and not the name of the subclass.  So for now it just
16558          * evaluates a well-known variable in DragDrop.
16559          * @method isTypeOfDD
16560          * @param {Object} the object to evaluate
16561          * @return {boolean} true if typeof oDD = DragDrop
16562          * @static
16563          */
16564         isTypeOfDD: function (oDD) {
16565             return (oDD && oDD.__ygDragDrop);
16566         },
16567
16568         /**
16569          * Utility function to determine if a given element has been
16570          * registered as a drag drop handle for the given Drag Drop object.
16571          * @method isHandle
16572          * @param {String} id the element id to check
16573          * @return {boolean} true if this element is a DragDrop handle, false
16574          * otherwise
16575          * @static
16576          */
16577         isHandle: function(sDDId, sHandleId) {
16578             return ( this.handleIds[sDDId] &&
16579                             this.handleIds[sDDId][sHandleId] );
16580         },
16581
16582         /**
16583          * Returns the DragDrop instance for a given id
16584          * @method getDDById
16585          * @param {String} id the id of the DragDrop object
16586          * @return {DragDrop} the drag drop object, null if it is not found
16587          * @static
16588          */
16589         getDDById: function(id) {
16590             for (var i in this.ids) {
16591                 if (this.ids[i][id]) {
16592                     return this.ids[i][id];
16593                 }
16594             }
16595             return null;
16596         },
16597
16598         /**
16599          * Fired after a registered DragDrop object gets the mousedown event.
16600          * Sets up the events required to track the object being dragged
16601          * @method handleMouseDown
16602          * @param {Event} e the event
16603          * @param oDD the DragDrop object being dragged
16604          * @private
16605          * @static
16606          */
16607         handleMouseDown: function(e, oDD) {
16608             if(Roo.QuickTips){
16609                 Roo.QuickTips.disable();
16610             }
16611             this.currentTarget = e.getTarget();
16612
16613             this.dragCurrent = oDD;
16614
16615             var el = oDD.getEl();
16616
16617             // track start position
16618             this.startX = e.getPageX();
16619             this.startY = e.getPageY();
16620
16621             this.deltaX = this.startX - el.offsetLeft;
16622             this.deltaY = this.startY - el.offsetTop;
16623
16624             this.dragThreshMet = false;
16625
16626             this.clickTimeout = setTimeout(
16627                     function() {
16628                         var DDM = Roo.dd.DDM;
16629                         DDM.startDrag(DDM.startX, DDM.startY);
16630                     },
16631                     this.clickTimeThresh );
16632         },
16633
16634         /**
16635          * Fired when either the drag pixel threshol or the mousedown hold
16636          * time threshold has been met.
16637          * @method startDrag
16638          * @param x {int} the X position of the original mousedown
16639          * @param y {int} the Y position of the original mousedown
16640          * @static
16641          */
16642         startDrag: function(x, y) {
16643             clearTimeout(this.clickTimeout);
16644             if (this.dragCurrent) {
16645                 this.dragCurrent.b4StartDrag(x, y);
16646                 this.dragCurrent.startDrag(x, y);
16647             }
16648             this.dragThreshMet = true;
16649         },
16650
16651         /**
16652          * Internal function to handle the mouseup event.  Will be invoked
16653          * from the context of the document.
16654          * @method handleMouseUp
16655          * @param {Event} e the event
16656          * @private
16657          * @static
16658          */
16659         handleMouseUp: function(e) {
16660
16661             if(Roo.QuickTips){
16662                 Roo.QuickTips.enable();
16663             }
16664             if (! this.dragCurrent) {
16665                 return;
16666             }
16667
16668             clearTimeout(this.clickTimeout);
16669
16670             if (this.dragThreshMet) {
16671                 this.fireEvents(e, true);
16672             } else {
16673             }
16674
16675             this.stopDrag(e);
16676
16677             this.stopEvent(e);
16678         },
16679
16680         /**
16681          * Utility to stop event propagation and event default, if these
16682          * features are turned on.
16683          * @method stopEvent
16684          * @param {Event} e the event as returned by this.getEvent()
16685          * @static
16686          */
16687         stopEvent: function(e){
16688             if(this.stopPropagation) {
16689                 e.stopPropagation();
16690             }
16691
16692             if (this.preventDefault) {
16693                 e.preventDefault();
16694             }
16695         },
16696
16697         /**
16698          * Internal function to clean up event handlers after the drag
16699          * operation is complete
16700          * @method stopDrag
16701          * @param {Event} e the event
16702          * @private
16703          * @static
16704          */
16705         stopDrag: function(e) {
16706             // Fire the drag end event for the item that was dragged
16707             if (this.dragCurrent) {
16708                 if (this.dragThreshMet) {
16709                     this.dragCurrent.b4EndDrag(e);
16710                     this.dragCurrent.endDrag(e);
16711                 }
16712
16713                 this.dragCurrent.onMouseUp(e);
16714             }
16715
16716             this.dragCurrent = null;
16717             this.dragOvers = {};
16718         },
16719
16720         /**
16721          * Internal function to handle the mousemove event.  Will be invoked
16722          * from the context of the html element.
16723          *
16724          * @TODO figure out what we can do about mouse events lost when the
16725          * user drags objects beyond the window boundary.  Currently we can
16726          * detect this in internet explorer by verifying that the mouse is
16727          * down during the mousemove event.  Firefox doesn't give us the
16728          * button state on the mousemove event.
16729          * @method handleMouseMove
16730          * @param {Event} e the event
16731          * @private
16732          * @static
16733          */
16734         handleMouseMove: function(e) {
16735             if (! this.dragCurrent) {
16736                 return true;
16737             }
16738
16739             // var button = e.which || e.button;
16740
16741             // check for IE mouseup outside of page boundary
16742             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16743                 this.stopEvent(e);
16744                 return this.handleMouseUp(e);
16745             }
16746
16747             if (!this.dragThreshMet) {
16748                 var diffX = Math.abs(this.startX - e.getPageX());
16749                 var diffY = Math.abs(this.startY - e.getPageY());
16750                 if (diffX > this.clickPixelThresh ||
16751                             diffY > this.clickPixelThresh) {
16752                     this.startDrag(this.startX, this.startY);
16753                 }
16754             }
16755
16756             if (this.dragThreshMet) {
16757                 this.dragCurrent.b4Drag(e);
16758                 this.dragCurrent.onDrag(e);
16759                 if(!this.dragCurrent.moveOnly){
16760                     this.fireEvents(e, false);
16761                 }
16762             }
16763
16764             this.stopEvent(e);
16765
16766             return true;
16767         },
16768
16769         /**
16770          * Iterates over all of the DragDrop elements to find ones we are
16771          * hovering over or dropping on
16772          * @method fireEvents
16773          * @param {Event} e the event
16774          * @param {boolean} isDrop is this a drop op or a mouseover op?
16775          * @private
16776          * @static
16777          */
16778         fireEvents: function(e, isDrop) {
16779             var dc = this.dragCurrent;
16780
16781             // If the user did the mouse up outside of the window, we could
16782             // get here even though we have ended the drag.
16783             if (!dc || dc.isLocked()) {
16784                 return;
16785             }
16786
16787             var pt = e.getPoint();
16788
16789             // cache the previous dragOver array
16790             var oldOvers = [];
16791
16792             var outEvts   = [];
16793             var overEvts  = [];
16794             var dropEvts  = [];
16795             var enterEvts = [];
16796
16797             // Check to see if the object(s) we were hovering over is no longer
16798             // being hovered over so we can fire the onDragOut event
16799             for (var i in this.dragOvers) {
16800
16801                 var ddo = this.dragOvers[i];
16802
16803                 if (! this.isTypeOfDD(ddo)) {
16804                     continue;
16805                 }
16806
16807                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16808                     outEvts.push( ddo );
16809                 }
16810
16811                 oldOvers[i] = true;
16812                 delete this.dragOvers[i];
16813             }
16814
16815             for (var sGroup in dc.groups) {
16816
16817                 if ("string" != typeof sGroup) {
16818                     continue;
16819                 }
16820
16821                 for (i in this.ids[sGroup]) {
16822                     var oDD = this.ids[sGroup][i];
16823                     if (! this.isTypeOfDD(oDD)) {
16824                         continue;
16825                     }
16826
16827                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16828                         if (this.isOverTarget(pt, oDD, this.mode)) {
16829                             // look for drop interactions
16830                             if (isDrop) {
16831                                 dropEvts.push( oDD );
16832                             // look for drag enter and drag over interactions
16833                             } else {
16834
16835                                 // initial drag over: dragEnter fires
16836                                 if (!oldOvers[oDD.id]) {
16837                                     enterEvts.push( oDD );
16838                                 // subsequent drag overs: dragOver fires
16839                                 } else {
16840                                     overEvts.push( oDD );
16841                                 }
16842
16843                                 this.dragOvers[oDD.id] = oDD;
16844                             }
16845                         }
16846                     }
16847                 }
16848             }
16849
16850             if (this.mode) {
16851                 if (outEvts.length) {
16852                     dc.b4DragOut(e, outEvts);
16853                     dc.onDragOut(e, outEvts);
16854                 }
16855
16856                 if (enterEvts.length) {
16857                     dc.onDragEnter(e, enterEvts);
16858                 }
16859
16860                 if (overEvts.length) {
16861                     dc.b4DragOver(e, overEvts);
16862                     dc.onDragOver(e, overEvts);
16863                 }
16864
16865                 if (dropEvts.length) {
16866                     dc.b4DragDrop(e, dropEvts);
16867                     dc.onDragDrop(e, dropEvts);
16868                 }
16869
16870             } else {
16871                 // fire dragout events
16872                 var len = 0;
16873                 for (i=0, len=outEvts.length; i<len; ++i) {
16874                     dc.b4DragOut(e, outEvts[i].id);
16875                     dc.onDragOut(e, outEvts[i].id);
16876                 }
16877
16878                 // fire enter events
16879                 for (i=0,len=enterEvts.length; i<len; ++i) {
16880                     // dc.b4DragEnter(e, oDD.id);
16881                     dc.onDragEnter(e, enterEvts[i].id);
16882                 }
16883
16884                 // fire over events
16885                 for (i=0,len=overEvts.length; i<len; ++i) {
16886                     dc.b4DragOver(e, overEvts[i].id);
16887                     dc.onDragOver(e, overEvts[i].id);
16888                 }
16889
16890                 // fire drop events
16891                 for (i=0, len=dropEvts.length; i<len; ++i) {
16892                     dc.b4DragDrop(e, dropEvts[i].id);
16893                     dc.onDragDrop(e, dropEvts[i].id);
16894                 }
16895
16896             }
16897
16898             // notify about a drop that did not find a target
16899             if (isDrop && !dropEvts.length) {
16900                 dc.onInvalidDrop(e);
16901             }
16902
16903         },
16904
16905         /**
16906          * Helper function for getting the best match from the list of drag
16907          * and drop objects returned by the drag and drop events when we are
16908          * in INTERSECT mode.  It returns either the first object that the
16909          * cursor is over, or the object that has the greatest overlap with
16910          * the dragged element.
16911          * @method getBestMatch
16912          * @param  {DragDrop[]} dds The array of drag and drop objects
16913          * targeted
16914          * @return {DragDrop}       The best single match
16915          * @static
16916          */
16917         getBestMatch: function(dds) {
16918             var winner = null;
16919             // Return null if the input is not what we expect
16920             //if (!dds || !dds.length || dds.length == 0) {
16921                // winner = null;
16922             // If there is only one item, it wins
16923             //} else if (dds.length == 1) {
16924
16925             var len = dds.length;
16926
16927             if (len == 1) {
16928                 winner = dds[0];
16929             } else {
16930                 // Loop through the targeted items
16931                 for (var i=0; i<len; ++i) {
16932                     var dd = dds[i];
16933                     // If the cursor is over the object, it wins.  If the
16934                     // cursor is over multiple matches, the first one we come
16935                     // to wins.
16936                     if (dd.cursorIsOver) {
16937                         winner = dd;
16938                         break;
16939                     // Otherwise the object with the most overlap wins
16940                     } else {
16941                         if (!winner ||
16942                             winner.overlap.getArea() < dd.overlap.getArea()) {
16943                             winner = dd;
16944                         }
16945                     }
16946                 }
16947             }
16948
16949             return winner;
16950         },
16951
16952         /**
16953          * Refreshes the cache of the top-left and bottom-right points of the
16954          * drag and drop objects in the specified group(s).  This is in the
16955          * format that is stored in the drag and drop instance, so typical
16956          * usage is:
16957          * <code>
16958          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16959          * </code>
16960          * Alternatively:
16961          * <code>
16962          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16963          * </code>
16964          * @TODO this really should be an indexed array.  Alternatively this
16965          * method could accept both.
16966          * @method refreshCache
16967          * @param {Object} groups an associative array of groups to refresh
16968          * @static
16969          */
16970         refreshCache: function(groups) {
16971             for (var sGroup in groups) {
16972                 if ("string" != typeof sGroup) {
16973                     continue;
16974                 }
16975                 for (var i in this.ids[sGroup]) {
16976                     var oDD = this.ids[sGroup][i];
16977
16978                     if (this.isTypeOfDD(oDD)) {
16979                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16980                         var loc = this.getLocation(oDD);
16981                         if (loc) {
16982                             this.locationCache[oDD.id] = loc;
16983                         } else {
16984                             delete this.locationCache[oDD.id];
16985                             // this will unregister the drag and drop object if
16986                             // the element is not in a usable state
16987                             // oDD.unreg();
16988                         }
16989                     }
16990                 }
16991             }
16992         },
16993
16994         /**
16995          * This checks to make sure an element exists and is in the DOM.  The
16996          * main purpose is to handle cases where innerHTML is used to remove
16997          * drag and drop objects from the DOM.  IE provides an 'unspecified
16998          * error' when trying to access the offsetParent of such an element
16999          * @method verifyEl
17000          * @param {HTMLElement} el the element to check
17001          * @return {boolean} true if the element looks usable
17002          * @static
17003          */
17004         verifyEl: function(el) {
17005             if (el) {
17006                 var parent;
17007                 if(Roo.isIE){
17008                     try{
17009                         parent = el.offsetParent;
17010                     }catch(e){}
17011                 }else{
17012                     parent = el.offsetParent;
17013                 }
17014                 if (parent) {
17015                     return true;
17016                 }
17017             }
17018
17019             return false;
17020         },
17021
17022         /**
17023          * Returns a Region object containing the drag and drop element's position
17024          * and size, including the padding configured for it
17025          * @method getLocation
17026          * @param {DragDrop} oDD the drag and drop object to get the
17027          *                       location for
17028          * @return {Roo.lib.Region} a Region object representing the total area
17029          *                             the element occupies, including any padding
17030          *                             the instance is configured for.
17031          * @static
17032          */
17033         getLocation: function(oDD) {
17034             if (! this.isTypeOfDD(oDD)) {
17035                 return null;
17036             }
17037
17038             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
17039
17040             try {
17041                 pos= Roo.lib.Dom.getXY(el);
17042             } catch (e) { }
17043
17044             if (!pos) {
17045                 return null;
17046             }
17047
17048             x1 = pos[0];
17049             x2 = x1 + el.offsetWidth;
17050             y1 = pos[1];
17051             y2 = y1 + el.offsetHeight;
17052
17053             t = y1 - oDD.padding[0];
17054             r = x2 + oDD.padding[1];
17055             b = y2 + oDD.padding[2];
17056             l = x1 - oDD.padding[3];
17057
17058             return new Roo.lib.Region( t, r, b, l );
17059         },
17060
17061         /**
17062          * Checks the cursor location to see if it over the target
17063          * @method isOverTarget
17064          * @param {Roo.lib.Point} pt The point to evaluate
17065          * @param {DragDrop} oTarget the DragDrop object we are inspecting
17066          * @return {boolean} true if the mouse is over the target
17067          * @private
17068          * @static
17069          */
17070         isOverTarget: function(pt, oTarget, intersect) {
17071             // use cache if available
17072             var loc = this.locationCache[oTarget.id];
17073             if (!loc || !this.useCache) {
17074                 loc = this.getLocation(oTarget);
17075                 this.locationCache[oTarget.id] = loc;
17076
17077             }
17078
17079             if (!loc) {
17080                 return false;
17081             }
17082
17083             oTarget.cursorIsOver = loc.contains( pt );
17084
17085             // DragDrop is using this as a sanity check for the initial mousedown
17086             // in this case we are done.  In POINT mode, if the drag obj has no
17087             // contraints, we are also done. Otherwise we need to evaluate the
17088             // location of the target as related to the actual location of the
17089             // dragged element.
17090             var dc = this.dragCurrent;
17091             if (!dc || !dc.getTargetCoord ||
17092                     (!intersect && !dc.constrainX && !dc.constrainY)) {
17093                 return oTarget.cursorIsOver;
17094             }
17095
17096             oTarget.overlap = null;
17097
17098             // Get the current location of the drag element, this is the
17099             // location of the mouse event less the delta that represents
17100             // where the original mousedown happened on the element.  We
17101             // need to consider constraints and ticks as well.
17102             var pos = dc.getTargetCoord(pt.x, pt.y);
17103
17104             var el = dc.getDragEl();
17105             var curRegion = new Roo.lib.Region( pos.y,
17106                                                    pos.x + el.offsetWidth,
17107                                                    pos.y + el.offsetHeight,
17108                                                    pos.x );
17109
17110             var overlap = curRegion.intersect(loc);
17111
17112             if (overlap) {
17113                 oTarget.overlap = overlap;
17114                 return (intersect) ? true : oTarget.cursorIsOver;
17115             } else {
17116                 return false;
17117             }
17118         },
17119
17120         /**
17121          * unload event handler
17122          * @method _onUnload
17123          * @private
17124          * @static
17125          */
17126         _onUnload: function(e, me) {
17127             Roo.dd.DragDropMgr.unregAll();
17128         },
17129
17130         /**
17131          * Cleans up the drag and drop events and objects.
17132          * @method unregAll
17133          * @private
17134          * @static
17135          */
17136         unregAll: function() {
17137
17138             if (this.dragCurrent) {
17139                 this.stopDrag();
17140                 this.dragCurrent = null;
17141             }
17142
17143             this._execOnAll("unreg", []);
17144
17145             for (i in this.elementCache) {
17146                 delete this.elementCache[i];
17147             }
17148
17149             this.elementCache = {};
17150             this.ids = {};
17151         },
17152
17153         /**
17154          * A cache of DOM elements
17155          * @property elementCache
17156          * @private
17157          * @static
17158          */
17159         elementCache: {},
17160
17161         /**
17162          * Get the wrapper for the DOM element specified
17163          * @method getElWrapper
17164          * @param {String} id the id of the element to get
17165          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
17166          * @private
17167          * @deprecated This wrapper isn't that useful
17168          * @static
17169          */
17170         getElWrapper: function(id) {
17171             var oWrapper = this.elementCache[id];
17172             if (!oWrapper || !oWrapper.el) {
17173                 oWrapper = this.elementCache[id] =
17174                     new this.ElementWrapper(Roo.getDom(id));
17175             }
17176             return oWrapper;
17177         },
17178
17179         /**
17180          * Returns the actual DOM element
17181          * @method getElement
17182          * @param {String} id the id of the elment to get
17183          * @return {Object} The element
17184          * @deprecated use Roo.getDom instead
17185          * @static
17186          */
17187         getElement: function(id) {
17188             return Roo.getDom(id);
17189         },
17190
17191         /**
17192          * Returns the style property for the DOM element (i.e.,
17193          * document.getElById(id).style)
17194          * @method getCss
17195          * @param {String} id the id of the elment to get
17196          * @return {Object} The style property of the element
17197          * @deprecated use Roo.getDom instead
17198          * @static
17199          */
17200         getCss: function(id) {
17201             var el = Roo.getDom(id);
17202             return (el) ? el.style : null;
17203         },
17204
17205         /**
17206          * Inner class for cached elements
17207          * @class DragDropMgr.ElementWrapper
17208          * @for DragDropMgr
17209          * @private
17210          * @deprecated
17211          */
17212         ElementWrapper: function(el) {
17213                 /**
17214                  * The element
17215                  * @property el
17216                  */
17217                 this.el = el || null;
17218                 /**
17219                  * The element id
17220                  * @property id
17221                  */
17222                 this.id = this.el && el.id;
17223                 /**
17224                  * A reference to the style property
17225                  * @property css
17226                  */
17227                 this.css = this.el && el.style;
17228             },
17229
17230         /**
17231          * Returns the X position of an html element
17232          * @method getPosX
17233          * @param el the element for which to get the position
17234          * @return {int} the X coordinate
17235          * @for DragDropMgr
17236          * @deprecated use Roo.lib.Dom.getX instead
17237          * @static
17238          */
17239         getPosX: function(el) {
17240             return Roo.lib.Dom.getX(el);
17241         },
17242
17243         /**
17244          * Returns the Y position of an html element
17245          * @method getPosY
17246          * @param el the element for which to get the position
17247          * @return {int} the Y coordinate
17248          * @deprecated use Roo.lib.Dom.getY instead
17249          * @static
17250          */
17251         getPosY: function(el) {
17252             return Roo.lib.Dom.getY(el);
17253         },
17254
17255         /**
17256          * Swap two nodes.  In IE, we use the native method, for others we
17257          * emulate the IE behavior
17258          * @method swapNode
17259          * @param n1 the first node to swap
17260          * @param n2 the other node to swap
17261          * @static
17262          */
17263         swapNode: function(n1, n2) {
17264             if (n1.swapNode) {
17265                 n1.swapNode(n2);
17266             } else {
17267                 var p = n2.parentNode;
17268                 var s = n2.nextSibling;
17269
17270                 if (s == n1) {
17271                     p.insertBefore(n1, n2);
17272                 } else if (n2 == n1.nextSibling) {
17273                     p.insertBefore(n2, n1);
17274                 } else {
17275                     n1.parentNode.replaceChild(n2, n1);
17276                     p.insertBefore(n1, s);
17277                 }
17278             }
17279         },
17280
17281         /**
17282          * Returns the current scroll position
17283          * @method getScroll
17284          * @private
17285          * @static
17286          */
17287         getScroll: function () {
17288             var t, l, dde=document.documentElement, db=document.body;
17289             if (dde && (dde.scrollTop || dde.scrollLeft)) {
17290                 t = dde.scrollTop;
17291                 l = dde.scrollLeft;
17292             } else if (db) {
17293                 t = db.scrollTop;
17294                 l = db.scrollLeft;
17295             } else {
17296
17297             }
17298             return { top: t, left: l };
17299         },
17300
17301         /**
17302          * Returns the specified element style property
17303          * @method getStyle
17304          * @param {HTMLElement} el          the element
17305          * @param {string}      styleProp   the style property
17306          * @return {string} The value of the style property
17307          * @deprecated use Roo.lib.Dom.getStyle
17308          * @static
17309          */
17310         getStyle: function(el, styleProp) {
17311             return Roo.fly(el).getStyle(styleProp);
17312         },
17313
17314         /**
17315          * Gets the scrollTop
17316          * @method getScrollTop
17317          * @return {int} the document's scrollTop
17318          * @static
17319          */
17320         getScrollTop: function () { return this.getScroll().top; },
17321
17322         /**
17323          * Gets the scrollLeft
17324          * @method getScrollLeft
17325          * @return {int} the document's scrollTop
17326          * @static
17327          */
17328         getScrollLeft: function () { return this.getScroll().left; },
17329
17330         /**
17331          * Sets the x/y position of an element to the location of the
17332          * target element.
17333          * @method moveToEl
17334          * @param {HTMLElement} moveEl      The element to move
17335          * @param {HTMLElement} targetEl    The position reference element
17336          * @static
17337          */
17338         moveToEl: function (moveEl, targetEl) {
17339             var aCoord = Roo.lib.Dom.getXY(targetEl);
17340             Roo.lib.Dom.setXY(moveEl, aCoord);
17341         },
17342
17343         /**
17344          * Numeric array sort function
17345          * @method numericSort
17346          * @static
17347          */
17348         numericSort: function(a, b) { return (a - b); },
17349
17350         /**
17351          * Internal counter
17352          * @property _timeoutCount
17353          * @private
17354          * @static
17355          */
17356         _timeoutCount: 0,
17357
17358         /**
17359          * Trying to make the load order less important.  Without this we get
17360          * an error if this file is loaded before the Event Utility.
17361          * @method _addListeners
17362          * @private
17363          * @static
17364          */
17365         _addListeners: function() {
17366             var DDM = Roo.dd.DDM;
17367             if ( Roo.lib.Event && document ) {
17368                 DDM._onLoad();
17369             } else {
17370                 if (DDM._timeoutCount > 2000) {
17371                 } else {
17372                     setTimeout(DDM._addListeners, 10);
17373                     if (document && document.body) {
17374                         DDM._timeoutCount += 1;
17375                     }
17376                 }
17377             }
17378         },
17379
17380         /**
17381          * Recursively searches the immediate parent and all child nodes for
17382          * the handle element in order to determine wheter or not it was
17383          * clicked.
17384          * @method handleWasClicked
17385          * @param node the html element to inspect
17386          * @static
17387          */
17388         handleWasClicked: function(node, id) {
17389             if (this.isHandle(id, node.id)) {
17390                 return true;
17391             } else {
17392                 // check to see if this is a text node child of the one we want
17393                 var p = node.parentNode;
17394
17395                 while (p) {
17396                     if (this.isHandle(id, p.id)) {
17397                         return true;
17398                     } else {
17399                         p = p.parentNode;
17400                     }
17401                 }
17402             }
17403
17404             return false;
17405         }
17406
17407     };
17408
17409 }();
17410
17411 // shorter alias, save a few bytes
17412 Roo.dd.DDM = Roo.dd.DragDropMgr;
17413 Roo.dd.DDM._addListeners();
17414
17415 }/*
17416  * Based on:
17417  * Ext JS Library 1.1.1
17418  * Copyright(c) 2006-2007, Ext JS, LLC.
17419  *
17420  * Originally Released Under LGPL - original licence link has changed is not relivant.
17421  *
17422  * Fork - LGPL
17423  * <script type="text/javascript">
17424  */
17425
17426 /**
17427  * @class Roo.dd.DD
17428  * A DragDrop implementation where the linked element follows the
17429  * mouse cursor during a drag.
17430  * @extends Roo.dd.DragDrop
17431  * @constructor
17432  * @param {String} id the id of the linked element
17433  * @param {String} sGroup the group of related DragDrop items
17434  * @param {object} config an object containing configurable attributes
17435  *                Valid properties for DD:
17436  *                    scroll
17437  */
17438 Roo.dd.DD = function(id, sGroup, config) {
17439     if (id) {
17440         this.init(id, sGroup, config);
17441     }
17442 };
17443
17444 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
17445
17446     /**
17447      * When set to true, the utility automatically tries to scroll the browser
17448      * window wehn a drag and drop element is dragged near the viewport boundary.
17449      * Defaults to true.
17450      * @property scroll
17451      * @type boolean
17452      */
17453     scroll: true,
17454
17455     /**
17456      * Sets the pointer offset to the distance between the linked element's top
17457      * left corner and the location the element was clicked
17458      * @method autoOffset
17459      * @param {int} iPageX the X coordinate of the click
17460      * @param {int} iPageY the Y coordinate of the click
17461      */
17462     autoOffset: function(iPageX, iPageY) {
17463         var x = iPageX - this.startPageX;
17464         var y = iPageY - this.startPageY;
17465         this.setDelta(x, y);
17466     },
17467
17468     /**
17469      * Sets the pointer offset.  You can call this directly to force the
17470      * offset to be in a particular location (e.g., pass in 0,0 to set it
17471      * to the center of the object)
17472      * @method setDelta
17473      * @param {int} iDeltaX the distance from the left
17474      * @param {int} iDeltaY the distance from the top
17475      */
17476     setDelta: function(iDeltaX, iDeltaY) {
17477         this.deltaX = iDeltaX;
17478         this.deltaY = iDeltaY;
17479     },
17480
17481     /**
17482      * Sets the drag element to the location of the mousedown or click event,
17483      * maintaining the cursor location relative to the location on the element
17484      * that was clicked.  Override this if you want to place the element in a
17485      * location other than where the cursor is.
17486      * @method setDragElPos
17487      * @param {int} iPageX the X coordinate of the mousedown or drag event
17488      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17489      */
17490     setDragElPos: function(iPageX, iPageY) {
17491         // the first time we do this, we are going to check to make sure
17492         // the element has css positioning
17493
17494         var el = this.getDragEl();
17495         this.alignElWithMouse(el, iPageX, iPageY);
17496     },
17497
17498     /**
17499      * Sets the element to the location of the mousedown or click event,
17500      * maintaining the cursor location relative to the location on the element
17501      * that was clicked.  Override this if you want to place the element in a
17502      * location other than where the cursor is.
17503      * @method alignElWithMouse
17504      * @param {HTMLElement} el the element to move
17505      * @param {int} iPageX the X coordinate of the mousedown or drag event
17506      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17507      */
17508     alignElWithMouse: function(el, iPageX, iPageY) {
17509         var oCoord = this.getTargetCoord(iPageX, iPageY);
17510         var fly = el.dom ? el : Roo.fly(el);
17511         if (!this.deltaSetXY) {
17512             var aCoord = [oCoord.x, oCoord.y];
17513             fly.setXY(aCoord);
17514             var newLeft = fly.getLeft(true);
17515             var newTop  = fly.getTop(true);
17516             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17517         } else {
17518             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17519         }
17520
17521         this.cachePosition(oCoord.x, oCoord.y);
17522         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17523         return oCoord;
17524     },
17525
17526     /**
17527      * Saves the most recent position so that we can reset the constraints and
17528      * tick marks on-demand.  We need to know this so that we can calculate the
17529      * number of pixels the element is offset from its original position.
17530      * @method cachePosition
17531      * @param iPageX the current x position (optional, this just makes it so we
17532      * don't have to look it up again)
17533      * @param iPageY the current y position (optional, this just makes it so we
17534      * don't have to look it up again)
17535      */
17536     cachePosition: function(iPageX, iPageY) {
17537         if (iPageX) {
17538             this.lastPageX = iPageX;
17539             this.lastPageY = iPageY;
17540         } else {
17541             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17542             this.lastPageX = aCoord[0];
17543             this.lastPageY = aCoord[1];
17544         }
17545     },
17546
17547     /**
17548      * Auto-scroll the window if the dragged object has been moved beyond the
17549      * visible window boundary.
17550      * @method autoScroll
17551      * @param {int} x the drag element's x position
17552      * @param {int} y the drag element's y position
17553      * @param {int} h the height of the drag element
17554      * @param {int} w the width of the drag element
17555      * @private
17556      */
17557     autoScroll: function(x, y, h, w) {
17558
17559         if (this.scroll) {
17560             // The client height
17561             var clientH = Roo.lib.Dom.getViewWidth();
17562
17563             // The client width
17564             var clientW = Roo.lib.Dom.getViewHeight();
17565
17566             // The amt scrolled down
17567             var st = this.DDM.getScrollTop();
17568
17569             // The amt scrolled right
17570             var sl = this.DDM.getScrollLeft();
17571
17572             // Location of the bottom of the element
17573             var bot = h + y;
17574
17575             // Location of the right of the element
17576             var right = w + x;
17577
17578             // The distance from the cursor to the bottom of the visible area,
17579             // adjusted so that we don't scroll if the cursor is beyond the
17580             // element drag constraints
17581             var toBot = (clientH + st - y - this.deltaY);
17582
17583             // The distance from the cursor to the right of the visible area
17584             var toRight = (clientW + sl - x - this.deltaX);
17585
17586
17587             // How close to the edge the cursor must be before we scroll
17588             // var thresh = (document.all) ? 100 : 40;
17589             var thresh = 40;
17590
17591             // How many pixels to scroll per autoscroll op.  This helps to reduce
17592             // clunky scrolling. IE is more sensitive about this ... it needs this
17593             // value to be higher.
17594             var scrAmt = (document.all) ? 80 : 30;
17595
17596             // Scroll down if we are near the bottom of the visible page and the
17597             // obj extends below the crease
17598             if ( bot > clientH && toBot < thresh ) {
17599                 window.scrollTo(sl, st + scrAmt);
17600             }
17601
17602             // Scroll up if the window is scrolled down and the top of the object
17603             // goes above the top border
17604             if ( y < st && st > 0 && y - st < thresh ) {
17605                 window.scrollTo(sl, st - scrAmt);
17606             }
17607
17608             // Scroll right if the obj is beyond the right border and the cursor is
17609             // near the border.
17610             if ( right > clientW && toRight < thresh ) {
17611                 window.scrollTo(sl + scrAmt, st);
17612             }
17613
17614             // Scroll left if the window has been scrolled to the right and the obj
17615             // extends past the left border
17616             if ( x < sl && sl > 0 && x - sl < thresh ) {
17617                 window.scrollTo(sl - scrAmt, st);
17618             }
17619         }
17620     },
17621
17622     /**
17623      * Finds the location the element should be placed if we want to move
17624      * it to where the mouse location less the click offset would place us.
17625      * @method getTargetCoord
17626      * @param {int} iPageX the X coordinate of the click
17627      * @param {int} iPageY the Y coordinate of the click
17628      * @return an object that contains the coordinates (Object.x and Object.y)
17629      * @private
17630      */
17631     getTargetCoord: function(iPageX, iPageY) {
17632
17633
17634         var x = iPageX - this.deltaX;
17635         var y = iPageY - this.deltaY;
17636
17637         if (this.constrainX) {
17638             if (x < this.minX) { x = this.minX; }
17639             if (x > this.maxX) { x = this.maxX; }
17640         }
17641
17642         if (this.constrainY) {
17643             if (y < this.minY) { y = this.minY; }
17644             if (y > this.maxY) { y = this.maxY; }
17645         }
17646
17647         x = this.getTick(x, this.xTicks);
17648         y = this.getTick(y, this.yTicks);
17649
17650
17651         return {x:x, y:y};
17652     },
17653
17654     /*
17655      * Sets up config options specific to this class. Overrides
17656      * Roo.dd.DragDrop, but all versions of this method through the
17657      * inheritance chain are called
17658      */
17659     applyConfig: function() {
17660         Roo.dd.DD.superclass.applyConfig.call(this);
17661         this.scroll = (this.config.scroll !== false);
17662     },
17663
17664     /*
17665      * Event that fires prior to the onMouseDown event.  Overrides
17666      * Roo.dd.DragDrop.
17667      */
17668     b4MouseDown: function(e) {
17669         // this.resetConstraints();
17670         this.autoOffset(e.getPageX(),
17671                             e.getPageY());
17672     },
17673
17674     /*
17675      * Event that fires prior to the onDrag event.  Overrides
17676      * Roo.dd.DragDrop.
17677      */
17678     b4Drag: function(e) {
17679         this.setDragElPos(e.getPageX(),
17680                             e.getPageY());
17681     },
17682
17683     toString: function() {
17684         return ("DD " + this.id);
17685     }
17686
17687     //////////////////////////////////////////////////////////////////////////
17688     // Debugging ygDragDrop events that can be overridden
17689     //////////////////////////////////////////////////////////////////////////
17690     /*
17691     startDrag: function(x, y) {
17692     },
17693
17694     onDrag: function(e) {
17695     },
17696
17697     onDragEnter: function(e, id) {
17698     },
17699
17700     onDragOver: function(e, id) {
17701     },
17702
17703     onDragOut: function(e, id) {
17704     },
17705
17706     onDragDrop: function(e, id) {
17707     },
17708
17709     endDrag: function(e) {
17710     }
17711
17712     */
17713
17714 });/*
17715  * Based on:
17716  * Ext JS Library 1.1.1
17717  * Copyright(c) 2006-2007, Ext JS, LLC.
17718  *
17719  * Originally Released Under LGPL - original licence link has changed is not relivant.
17720  *
17721  * Fork - LGPL
17722  * <script type="text/javascript">
17723  */
17724
17725 /**
17726  * @class Roo.dd.DDProxy
17727  * A DragDrop implementation that inserts an empty, bordered div into
17728  * the document that follows the cursor during drag operations.  At the time of
17729  * the click, the frame div is resized to the dimensions of the linked html
17730  * element, and moved to the exact location of the linked element.
17731  *
17732  * References to the "frame" element refer to the single proxy element that
17733  * was created to be dragged in place of all DDProxy elements on the
17734  * page.
17735  *
17736  * @extends Roo.dd.DD
17737  * @constructor
17738  * @param {String} id the id of the linked html element
17739  * @param {String} sGroup the group of related DragDrop objects
17740  * @param {object} config an object containing configurable attributes
17741  *                Valid properties for DDProxy in addition to those in DragDrop:
17742  *                   resizeFrame, centerFrame, dragElId
17743  */
17744 Roo.dd.DDProxy = function(id, sGroup, config) {
17745     if (id) {
17746         this.init(id, sGroup, config);
17747         this.initFrame();
17748     }
17749 };
17750
17751 /**
17752  * The default drag frame div id
17753  * @property Roo.dd.DDProxy.dragElId
17754  * @type String
17755  * @static
17756  */
17757 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17758
17759 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17760
17761     /**
17762      * By default we resize the drag frame to be the same size as the element
17763      * we want to drag (this is to get the frame effect).  We can turn it off
17764      * if we want a different behavior.
17765      * @property resizeFrame
17766      * @type boolean
17767      */
17768     resizeFrame: true,
17769
17770     /**
17771      * By default the frame is positioned exactly where the drag element is, so
17772      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17773      * you do not have constraints on the obj is to have the drag frame centered
17774      * around the cursor.  Set centerFrame to true for this effect.
17775      * @property centerFrame
17776      * @type boolean
17777      */
17778     centerFrame: false,
17779
17780     /**
17781      * Creates the proxy element if it does not yet exist
17782      * @method createFrame
17783      */
17784     createFrame: function() {
17785         var self = this;
17786         var body = document.body;
17787
17788         if (!body || !body.firstChild) {
17789             setTimeout( function() { self.createFrame(); }, 50 );
17790             return;
17791         }
17792
17793         var div = this.getDragEl();
17794
17795         if (!div) {
17796             div    = document.createElement("div");
17797             div.id = this.dragElId;
17798             var s  = div.style;
17799
17800             s.position   = "absolute";
17801             s.visibility = "hidden";
17802             s.cursor     = "move";
17803             s.border     = "2px solid #aaa";
17804             s.zIndex     = 999;
17805
17806             // appendChild can blow up IE if invoked prior to the window load event
17807             // while rendering a table.  It is possible there are other scenarios
17808             // that would cause this to happen as well.
17809             body.insertBefore(div, body.firstChild);
17810         }
17811     },
17812
17813     /**
17814      * Initialization for the drag frame element.  Must be called in the
17815      * constructor of all subclasses
17816      * @method initFrame
17817      */
17818     initFrame: function() {
17819         this.createFrame();
17820     },
17821
17822     applyConfig: function() {
17823         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17824
17825         this.resizeFrame = (this.config.resizeFrame !== false);
17826         this.centerFrame = (this.config.centerFrame);
17827         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17828     },
17829
17830     /**
17831      * Resizes the drag frame to the dimensions of the clicked object, positions
17832      * it over the object, and finally displays it
17833      * @method showFrame
17834      * @param {int} iPageX X click position
17835      * @param {int} iPageY Y click position
17836      * @private
17837      */
17838     showFrame: function(iPageX, iPageY) {
17839         var el = this.getEl();
17840         var dragEl = this.getDragEl();
17841         var s = dragEl.style;
17842
17843         this._resizeProxy();
17844
17845         if (this.centerFrame) {
17846             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17847                            Math.round(parseInt(s.height, 10)/2) );
17848         }
17849
17850         this.setDragElPos(iPageX, iPageY);
17851
17852         Roo.fly(dragEl).show();
17853     },
17854
17855     /**
17856      * The proxy is automatically resized to the dimensions of the linked
17857      * element when a drag is initiated, unless resizeFrame is set to false
17858      * @method _resizeProxy
17859      * @private
17860      */
17861     _resizeProxy: function() {
17862         if (this.resizeFrame) {
17863             var el = this.getEl();
17864             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17865         }
17866     },
17867
17868     // overrides Roo.dd.DragDrop
17869     b4MouseDown: function(e) {
17870         var x = e.getPageX();
17871         var y = e.getPageY();
17872         this.autoOffset(x, y);
17873         this.setDragElPos(x, y);
17874     },
17875
17876     // overrides Roo.dd.DragDrop
17877     b4StartDrag: function(x, y) {
17878         // show the drag frame
17879         this.showFrame(x, y);
17880     },
17881
17882     // overrides Roo.dd.DragDrop
17883     b4EndDrag: function(e) {
17884         Roo.fly(this.getDragEl()).hide();
17885     },
17886
17887     // overrides Roo.dd.DragDrop
17888     // By default we try to move the element to the last location of the frame.
17889     // This is so that the default behavior mirrors that of Roo.dd.DD.
17890     endDrag: function(e) {
17891
17892         var lel = this.getEl();
17893         var del = this.getDragEl();
17894
17895         // Show the drag frame briefly so we can get its position
17896         del.style.visibility = "";
17897
17898         this.beforeMove();
17899         // Hide the linked element before the move to get around a Safari
17900         // rendering bug.
17901         lel.style.visibility = "hidden";
17902         Roo.dd.DDM.moveToEl(lel, del);
17903         del.style.visibility = "hidden";
17904         lel.style.visibility = "";
17905
17906         this.afterDrag();
17907     },
17908
17909     beforeMove : function(){
17910
17911     },
17912
17913     afterDrag : function(){
17914
17915     },
17916
17917     toString: function() {
17918         return ("DDProxy " + this.id);
17919     }
17920
17921 });
17922 /*
17923  * Based on:
17924  * Ext JS Library 1.1.1
17925  * Copyright(c) 2006-2007, Ext JS, LLC.
17926  *
17927  * Originally Released Under LGPL - original licence link has changed is not relivant.
17928  *
17929  * Fork - LGPL
17930  * <script type="text/javascript">
17931  */
17932
17933  /**
17934  * @class Roo.dd.DDTarget
17935  * A DragDrop implementation that does not move, but can be a drop
17936  * target.  You would get the same result by simply omitting implementation
17937  * for the event callbacks, but this way we reduce the processing cost of the
17938  * event listener and the callbacks.
17939  * @extends Roo.dd.DragDrop
17940  * @constructor
17941  * @param {String} id the id of the element that is a drop target
17942  * @param {String} sGroup the group of related DragDrop objects
17943  * @param {object} config an object containing configurable attributes
17944  *                 Valid properties for DDTarget in addition to those in
17945  *                 DragDrop:
17946  *                    none
17947  */
17948 Roo.dd.DDTarget = function(id, sGroup, config) {
17949     if (id) {
17950         this.initTarget(id, sGroup, config);
17951     }
17952     if (config.listeners || config.events) { 
17953        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17954             listeners : config.listeners || {}, 
17955             events : config.events || {} 
17956         });    
17957     }
17958 };
17959
17960 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17961 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17962     toString: function() {
17963         return ("DDTarget " + this.id);
17964     }
17965 });
17966 /*
17967  * Based on:
17968  * Ext JS Library 1.1.1
17969  * Copyright(c) 2006-2007, Ext JS, LLC.
17970  *
17971  * Originally Released Under LGPL - original licence link has changed is not relivant.
17972  *
17973  * Fork - LGPL
17974  * <script type="text/javascript">
17975  */
17976  
17977
17978 /**
17979  * @class Roo.dd.ScrollManager
17980  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17981  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17982  * @singleton
17983  */
17984 Roo.dd.ScrollManager = function(){
17985     var ddm = Roo.dd.DragDropMgr;
17986     var els = {};
17987     var dragEl = null;
17988     var proc = {};
17989     
17990     
17991     
17992     var onStop = function(e){
17993         dragEl = null;
17994         clearProc();
17995     };
17996     
17997     var triggerRefresh = function(){
17998         if(ddm.dragCurrent){
17999              ddm.refreshCache(ddm.dragCurrent.groups);
18000         }
18001     };
18002     
18003     var doScroll = function(){
18004         if(ddm.dragCurrent){
18005             var dds = Roo.dd.ScrollManager;
18006             if(!dds.animate){
18007                 if(proc.el.scroll(proc.dir, dds.increment)){
18008                     triggerRefresh();
18009                 }
18010             }else{
18011                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
18012             }
18013         }
18014     };
18015     
18016     var clearProc = function(){
18017         if(proc.id){
18018             clearInterval(proc.id);
18019         }
18020         proc.id = 0;
18021         proc.el = null;
18022         proc.dir = "";
18023     };
18024     
18025     var startProc = function(el, dir){
18026          Roo.log('scroll startproc');
18027         clearProc();
18028         proc.el = el;
18029         proc.dir = dir;
18030         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
18031     };
18032     
18033     var onFire = function(e, isDrop){
18034        
18035         if(isDrop || !ddm.dragCurrent){ return; }
18036         var dds = Roo.dd.ScrollManager;
18037         if(!dragEl || dragEl != ddm.dragCurrent){
18038             dragEl = ddm.dragCurrent;
18039             // refresh regions on drag start
18040             dds.refreshCache();
18041         }
18042         
18043         var xy = Roo.lib.Event.getXY(e);
18044         var pt = new Roo.lib.Point(xy[0], xy[1]);
18045         for(var id in els){
18046             var el = els[id], r = el._region;
18047             if(r && r.contains(pt) && el.isScrollable()){
18048                 if(r.bottom - pt.y <= dds.thresh){
18049                     if(proc.el != el){
18050                         startProc(el, "down");
18051                     }
18052                     return;
18053                 }else if(r.right - pt.x <= dds.thresh){
18054                     if(proc.el != el){
18055                         startProc(el, "left");
18056                     }
18057                     return;
18058                 }else if(pt.y - r.top <= dds.thresh){
18059                     if(proc.el != el){
18060                         startProc(el, "up");
18061                     }
18062                     return;
18063                 }else if(pt.x - r.left <= dds.thresh){
18064                     if(proc.el != el){
18065                         startProc(el, "right");
18066                     }
18067                     return;
18068                 }
18069             }
18070         }
18071         clearProc();
18072     };
18073     
18074     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
18075     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
18076     
18077     return {
18078         /**
18079          * Registers new overflow element(s) to auto scroll
18080          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
18081          */
18082         register : function(el){
18083             if(el instanceof Array){
18084                 for(var i = 0, len = el.length; i < len; i++) {
18085                         this.register(el[i]);
18086                 }
18087             }else{
18088                 el = Roo.get(el);
18089                 els[el.id] = el;
18090             }
18091             Roo.dd.ScrollManager.els = els;
18092         },
18093         
18094         /**
18095          * Unregisters overflow element(s) so they are no longer scrolled
18096          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
18097          */
18098         unregister : function(el){
18099             if(el instanceof Array){
18100                 for(var i = 0, len = el.length; i < len; i++) {
18101                         this.unregister(el[i]);
18102                 }
18103             }else{
18104                 el = Roo.get(el);
18105                 delete els[el.id];
18106             }
18107         },
18108         
18109         /**
18110          * The number of pixels from the edge of a container the pointer needs to be to 
18111          * trigger scrolling (defaults to 25)
18112          * @type Number
18113          */
18114         thresh : 25,
18115         
18116         /**
18117          * The number of pixels to scroll in each scroll increment (defaults to 50)
18118          * @type Number
18119          */
18120         increment : 100,
18121         
18122         /**
18123          * The frequency of scrolls in milliseconds (defaults to 500)
18124          * @type Number
18125          */
18126         frequency : 500,
18127         
18128         /**
18129          * True to animate the scroll (defaults to true)
18130          * @type Boolean
18131          */
18132         animate: true,
18133         
18134         /**
18135          * The animation duration in seconds - 
18136          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
18137          * @type Number
18138          */
18139         animDuration: .4,
18140         
18141         /**
18142          * Manually trigger a cache refresh.
18143          */
18144         refreshCache : function(){
18145             for(var id in els){
18146                 if(typeof els[id] == 'object'){ // for people extending the object prototype
18147                     els[id]._region = els[id].getRegion();
18148                 }
18149             }
18150         }
18151     };
18152 }();/*
18153  * Based on:
18154  * Ext JS Library 1.1.1
18155  * Copyright(c) 2006-2007, Ext JS, LLC.
18156  *
18157  * Originally Released Under LGPL - original licence link has changed is not relivant.
18158  *
18159  * Fork - LGPL
18160  * <script type="text/javascript">
18161  */
18162  
18163
18164 /**
18165  * @class Roo.dd.Registry
18166  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
18167  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
18168  * @singleton
18169  */
18170 Roo.dd.Registry = function(){
18171     var elements = {}; 
18172     var handles = {}; 
18173     var autoIdSeed = 0;
18174
18175     var getId = function(el, autogen){
18176         if(typeof el == "string"){
18177             return el;
18178         }
18179         var id = el.id;
18180         if(!id && autogen !== false){
18181             id = "roodd-" + (++autoIdSeed);
18182             el.id = id;
18183         }
18184         return id;
18185     };
18186     
18187     return {
18188     /**
18189      * Register a drag drop element
18190      * @param {String|HTMLElement} element The id or DOM node to register
18191      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
18192      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
18193      * knows how to interpret, plus there are some specific properties known to the Registry that should be
18194      * populated in the data object (if applicable):
18195      * <pre>
18196 Value      Description<br />
18197 ---------  ------------------------------------------<br />
18198 handles    Array of DOM nodes that trigger dragging<br />
18199            for the element being registered<br />
18200 isHandle   True if the element passed in triggers<br />
18201            dragging itself, else false
18202 </pre>
18203      */
18204         register : function(el, data){
18205             data = data || {};
18206             if(typeof el == "string"){
18207                 el = document.getElementById(el);
18208             }
18209             data.ddel = el;
18210             elements[getId(el)] = data;
18211             if(data.isHandle !== false){
18212                 handles[data.ddel.id] = data;
18213             }
18214             if(data.handles){
18215                 var hs = data.handles;
18216                 for(var i = 0, len = hs.length; i < len; i++){
18217                         handles[getId(hs[i])] = data;
18218                 }
18219             }
18220         },
18221
18222     /**
18223      * Unregister a drag drop element
18224      * @param {String|HTMLElement}  element The id or DOM node to unregister
18225      */
18226         unregister : function(el){
18227             var id = getId(el, false);
18228             var data = elements[id];
18229             if(data){
18230                 delete elements[id];
18231                 if(data.handles){
18232                     var hs = data.handles;
18233                     for(var i = 0, len = hs.length; i < len; i++){
18234                         delete handles[getId(hs[i], false)];
18235                     }
18236                 }
18237             }
18238         },
18239
18240     /**
18241      * Returns the handle registered for a DOM Node by id
18242      * @param {String|HTMLElement} id The DOM node or id to look up
18243      * @return {Object} handle The custom handle data
18244      */
18245         getHandle : function(id){
18246             if(typeof id != "string"){ // must be element?
18247                 id = id.id;
18248             }
18249             return handles[id];
18250         },
18251
18252     /**
18253      * Returns the handle that is registered for the DOM node that is the target of the event
18254      * @param {Event} e The event
18255      * @return {Object} handle The custom handle data
18256      */
18257         getHandleFromEvent : function(e){
18258             var t = Roo.lib.Event.getTarget(e);
18259             return t ? handles[t.id] : null;
18260         },
18261
18262     /**
18263      * Returns a custom data object that is registered for a DOM node by id
18264      * @param {String|HTMLElement} id The DOM node or id to look up
18265      * @return {Object} data The custom data
18266      */
18267         getTarget : function(id){
18268             if(typeof id != "string"){ // must be element?
18269                 id = id.id;
18270             }
18271             return elements[id];
18272         },
18273
18274     /**
18275      * Returns a custom data object that is registered for the DOM node that is the target of the event
18276      * @param {Event} e The event
18277      * @return {Object} data The custom data
18278      */
18279         getTargetFromEvent : function(e){
18280             var t = Roo.lib.Event.getTarget(e);
18281             return t ? elements[t.id] || handles[t.id] : null;
18282         }
18283     };
18284 }();/*
18285  * Based on:
18286  * Ext JS Library 1.1.1
18287  * Copyright(c) 2006-2007, Ext JS, LLC.
18288  *
18289  * Originally Released Under LGPL - original licence link has changed is not relivant.
18290  *
18291  * Fork - LGPL
18292  * <script type="text/javascript">
18293  */
18294  
18295
18296 /**
18297  * @class Roo.dd.StatusProxy
18298  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
18299  * default drag proxy used by all Roo.dd components.
18300  * @constructor
18301  * @param {Object} config
18302  */
18303 Roo.dd.StatusProxy = function(config){
18304     Roo.apply(this, config);
18305     this.id = this.id || Roo.id();
18306     this.el = new Roo.Layer({
18307         dh: {
18308             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
18309                 {tag: "div", cls: "x-dd-drop-icon"},
18310                 {tag: "div", cls: "x-dd-drag-ghost"}
18311             ]
18312         }, 
18313         shadow: !config || config.shadow !== false
18314     });
18315     this.ghost = Roo.get(this.el.dom.childNodes[1]);
18316     this.dropStatus = this.dropNotAllowed;
18317 };
18318
18319 Roo.dd.StatusProxy.prototype = {
18320     /**
18321      * @cfg {String} dropAllowed
18322      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
18323      */
18324     dropAllowed : "x-dd-drop-ok",
18325     /**
18326      * @cfg {String} dropNotAllowed
18327      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
18328      */
18329     dropNotAllowed : "x-dd-drop-nodrop",
18330
18331     /**
18332      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
18333      * over the current target element.
18334      * @param {String} cssClass The css class for the new drop status indicator image
18335      */
18336     setStatus : function(cssClass){
18337         cssClass = cssClass || this.dropNotAllowed;
18338         if(this.dropStatus != cssClass){
18339             this.el.replaceClass(this.dropStatus, cssClass);
18340             this.dropStatus = cssClass;
18341         }
18342     },
18343
18344     /**
18345      * Resets the status indicator to the default dropNotAllowed value
18346      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
18347      */
18348     reset : function(clearGhost){
18349         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
18350         this.dropStatus = this.dropNotAllowed;
18351         if(clearGhost){
18352             this.ghost.update("");
18353         }
18354     },
18355
18356     /**
18357      * Updates the contents of the ghost element
18358      * @param {String} html The html that will replace the current innerHTML of the ghost element
18359      */
18360     update : function(html){
18361         if(typeof html == "string"){
18362             this.ghost.update(html);
18363         }else{
18364             this.ghost.update("");
18365             html.style.margin = "0";
18366             this.ghost.dom.appendChild(html);
18367         }
18368         // ensure float = none set?? cant remember why though.
18369         var el = this.ghost.dom.firstChild;
18370                 if(el){
18371                         Roo.fly(el).setStyle('float', 'none');
18372                 }
18373     },
18374     
18375     /**
18376      * Returns the underlying proxy {@link Roo.Layer}
18377      * @return {Roo.Layer} el
18378     */
18379     getEl : function(){
18380         return this.el;
18381     },
18382
18383     /**
18384      * Returns the ghost element
18385      * @return {Roo.Element} el
18386      */
18387     getGhost : function(){
18388         return this.ghost;
18389     },
18390
18391     /**
18392      * Hides the proxy
18393      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
18394      */
18395     hide : function(clear){
18396         this.el.hide();
18397         if(clear){
18398             this.reset(true);
18399         }
18400     },
18401
18402     /**
18403      * Stops the repair animation if it's currently running
18404      */
18405     stop : function(){
18406         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
18407             this.anim.stop();
18408         }
18409     },
18410
18411     /**
18412      * Displays this proxy
18413      */
18414     show : function(){
18415         this.el.show();
18416     },
18417
18418     /**
18419      * Force the Layer to sync its shadow and shim positions to the element
18420      */
18421     sync : function(){
18422         this.el.sync();
18423     },
18424
18425     /**
18426      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
18427      * invalid drop operation by the item being dragged.
18428      * @param {Array} xy The XY position of the element ([x, y])
18429      * @param {Function} callback The function to call after the repair is complete
18430      * @param {Object} scope The scope in which to execute the callback
18431      */
18432     repair : function(xy, callback, scope){
18433         this.callback = callback;
18434         this.scope = scope;
18435         if(xy && this.animRepair !== false){
18436             this.el.addClass("x-dd-drag-repair");
18437             this.el.hideUnders(true);
18438             this.anim = this.el.shift({
18439                 duration: this.repairDuration || .5,
18440                 easing: 'easeOut',
18441                 xy: xy,
18442                 stopFx: true,
18443                 callback: this.afterRepair,
18444                 scope: this
18445             });
18446         }else{
18447             this.afterRepair();
18448         }
18449     },
18450
18451     // private
18452     afterRepair : function(){
18453         this.hide(true);
18454         if(typeof this.callback == "function"){
18455             this.callback.call(this.scope || this);
18456         }
18457         this.callback = null;
18458         this.scope = null;
18459     }
18460 };/*
18461  * Based on:
18462  * Ext JS Library 1.1.1
18463  * Copyright(c) 2006-2007, Ext JS, LLC.
18464  *
18465  * Originally Released Under LGPL - original licence link has changed is not relivant.
18466  *
18467  * Fork - LGPL
18468  * <script type="text/javascript">
18469  */
18470
18471 /**
18472  * @class Roo.dd.DragSource
18473  * @extends Roo.dd.DDProxy
18474  * A simple class that provides the basic implementation needed to make any element draggable.
18475  * @constructor
18476  * @param {String/HTMLElement/Element} el The container element
18477  * @param {Object} config
18478  */
18479 Roo.dd.DragSource = function(el, config){
18480     this.el = Roo.get(el);
18481     this.dragData = {};
18482     
18483     Roo.apply(this, config);
18484     
18485     if(!this.proxy){
18486         this.proxy = new Roo.dd.StatusProxy();
18487     }
18488
18489     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18490           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18491     
18492     this.dragging = false;
18493 };
18494
18495 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18496     /**
18497      * @cfg {String} dropAllowed
18498      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18499      */
18500     dropAllowed : "x-dd-drop-ok",
18501     /**
18502      * @cfg {String} dropNotAllowed
18503      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18504      */
18505     dropNotAllowed : "x-dd-drop-nodrop",
18506
18507     /**
18508      * Returns the data object associated with this drag source
18509      * @return {Object} data An object containing arbitrary data
18510      */
18511     getDragData : function(e){
18512         return this.dragData;
18513     },
18514
18515     // private
18516     onDragEnter : function(e, id){
18517         var target = Roo.dd.DragDropMgr.getDDById(id);
18518         this.cachedTarget = target;
18519         if(this.beforeDragEnter(target, e, id) !== false){
18520             if(target.isNotifyTarget){
18521                 var status = target.notifyEnter(this, e, this.dragData);
18522                 this.proxy.setStatus(status);
18523             }else{
18524                 this.proxy.setStatus(this.dropAllowed);
18525             }
18526             
18527             if(this.afterDragEnter){
18528                 /**
18529                  * An empty function by default, but provided so that you can perform a custom action
18530                  * when the dragged item enters the drop target by providing an implementation.
18531                  * @param {Roo.dd.DragDrop} target The drop target
18532                  * @param {Event} e The event object
18533                  * @param {String} id The id of the dragged element
18534                  * @method afterDragEnter
18535                  */
18536                 this.afterDragEnter(target, e, id);
18537             }
18538         }
18539     },
18540
18541     /**
18542      * An empty function by default, but provided so that you can perform a custom action
18543      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18544      * @param {Roo.dd.DragDrop} target The drop target
18545      * @param {Event} e The event object
18546      * @param {String} id The id of the dragged element
18547      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18548      */
18549     beforeDragEnter : function(target, e, id){
18550         return true;
18551     },
18552
18553     // private
18554     alignElWithMouse: function() {
18555         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18556         this.proxy.sync();
18557     },
18558
18559     // private
18560     onDragOver : function(e, id){
18561         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18562         if(this.beforeDragOver(target, e, id) !== false){
18563             if(target.isNotifyTarget){
18564                 var status = target.notifyOver(this, e, this.dragData);
18565                 this.proxy.setStatus(status);
18566             }
18567
18568             if(this.afterDragOver){
18569                 /**
18570                  * An empty function by default, but provided so that you can perform a custom action
18571                  * while the dragged item is over the drop target by providing an implementation.
18572                  * @param {Roo.dd.DragDrop} target The drop target
18573                  * @param {Event} e The event object
18574                  * @param {String} id The id of the dragged element
18575                  * @method afterDragOver
18576                  */
18577                 this.afterDragOver(target, e, id);
18578             }
18579         }
18580     },
18581
18582     /**
18583      * An empty function by default, but provided so that you can perform a custom action
18584      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18585      * @param {Roo.dd.DragDrop} target The drop target
18586      * @param {Event} e The event object
18587      * @param {String} id The id of the dragged element
18588      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18589      */
18590     beforeDragOver : function(target, e, id){
18591         return true;
18592     },
18593
18594     // private
18595     onDragOut : function(e, id){
18596         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18597         if(this.beforeDragOut(target, e, id) !== false){
18598             if(target.isNotifyTarget){
18599                 target.notifyOut(this, e, this.dragData);
18600             }
18601             this.proxy.reset();
18602             if(this.afterDragOut){
18603                 /**
18604                  * An empty function by default, but provided so that you can perform a custom action
18605                  * after the dragged item is dragged out of the target without dropping.
18606                  * @param {Roo.dd.DragDrop} target The drop target
18607                  * @param {Event} e The event object
18608                  * @param {String} id The id of the dragged element
18609                  * @method afterDragOut
18610                  */
18611                 this.afterDragOut(target, e, id);
18612             }
18613         }
18614         this.cachedTarget = null;
18615     },
18616
18617     /**
18618      * An empty function by default, but provided so that you can perform a custom action before the dragged
18619      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18620      * @param {Roo.dd.DragDrop} target The drop target
18621      * @param {Event} e The event object
18622      * @param {String} id The id of the dragged element
18623      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18624      */
18625     beforeDragOut : function(target, e, id){
18626         return true;
18627     },
18628     
18629     // private
18630     onDragDrop : function(e, id){
18631         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18632         if(this.beforeDragDrop(target, e, id) !== false){
18633             if(target.isNotifyTarget){
18634                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18635                     this.onValidDrop(target, e, id);
18636                 }else{
18637                     this.onInvalidDrop(target, e, id);
18638                 }
18639             }else{
18640                 this.onValidDrop(target, e, id);
18641             }
18642             
18643             if(this.afterDragDrop){
18644                 /**
18645                  * An empty function by default, but provided so that you can perform a custom action
18646                  * after a valid drag drop has occurred by providing an implementation.
18647                  * @param {Roo.dd.DragDrop} target The drop target
18648                  * @param {Event} e The event object
18649                  * @param {String} id The id of the dropped element
18650                  * @method afterDragDrop
18651                  */
18652                 this.afterDragDrop(target, e, id);
18653             }
18654         }
18655         delete this.cachedTarget;
18656     },
18657
18658     /**
18659      * An empty function by default, but provided so that you can perform a custom action before the dragged
18660      * item is dropped onto the target and optionally cancel the onDragDrop.
18661      * @param {Roo.dd.DragDrop} target The drop target
18662      * @param {Event} e The event object
18663      * @param {String} id The id of the dragged element
18664      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18665      */
18666     beforeDragDrop : function(target, e, id){
18667         return true;
18668     },
18669
18670     // private
18671     onValidDrop : function(target, e, id){
18672         this.hideProxy();
18673         if(this.afterValidDrop){
18674             /**
18675              * An empty function by default, but provided so that you can perform a custom action
18676              * after a valid drop has occurred by providing an implementation.
18677              * @param {Object} target The target DD 
18678              * @param {Event} e The event object
18679              * @param {String} id The id of the dropped element
18680              * @method afterInvalidDrop
18681              */
18682             this.afterValidDrop(target, e, id);
18683         }
18684     },
18685
18686     // private
18687     getRepairXY : function(e, data){
18688         return this.el.getXY();  
18689     },
18690
18691     // private
18692     onInvalidDrop : function(target, e, id){
18693         this.beforeInvalidDrop(target, e, id);
18694         if(this.cachedTarget){
18695             if(this.cachedTarget.isNotifyTarget){
18696                 this.cachedTarget.notifyOut(this, e, this.dragData);
18697             }
18698             this.cacheTarget = null;
18699         }
18700         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18701
18702         if(this.afterInvalidDrop){
18703             /**
18704              * An empty function by default, but provided so that you can perform a custom action
18705              * after an invalid drop has occurred by providing an implementation.
18706              * @param {Event} e The event object
18707              * @param {String} id The id of the dropped element
18708              * @method afterInvalidDrop
18709              */
18710             this.afterInvalidDrop(e, id);
18711         }
18712     },
18713
18714     // private
18715     afterRepair : function(){
18716         if(Roo.enableFx){
18717             this.el.highlight(this.hlColor || "c3daf9");
18718         }
18719         this.dragging = false;
18720     },
18721
18722     /**
18723      * An empty function by default, but provided so that you can perform a custom action after an invalid
18724      * drop has occurred.
18725      * @param {Roo.dd.DragDrop} target The drop target
18726      * @param {Event} e The event object
18727      * @param {String} id The id of the dragged element
18728      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18729      */
18730     beforeInvalidDrop : function(target, e, id){
18731         return true;
18732     },
18733
18734     // private
18735     handleMouseDown : function(e){
18736         if(this.dragging) {
18737             return;
18738         }
18739         var data = this.getDragData(e);
18740         if(data && this.onBeforeDrag(data, e) !== false){
18741             this.dragData = data;
18742             this.proxy.stop();
18743             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18744         } 
18745     },
18746
18747     /**
18748      * An empty function by default, but provided so that you can perform a custom action before the initial
18749      * drag event begins and optionally cancel it.
18750      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18751      * @param {Event} e The event object
18752      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18753      */
18754     onBeforeDrag : function(data, e){
18755         return true;
18756     },
18757
18758     /**
18759      * An empty function by default, but provided so that you can perform a custom action once the initial
18760      * drag event has begun.  The drag cannot be canceled from this function.
18761      * @param {Number} x The x position of the click on the dragged object
18762      * @param {Number} y The y position of the click on the dragged object
18763      */
18764     onStartDrag : Roo.emptyFn,
18765
18766     // private - YUI override
18767     startDrag : function(x, y){
18768         this.proxy.reset();
18769         this.dragging = true;
18770         this.proxy.update("");
18771         this.onInitDrag(x, y);
18772         this.proxy.show();
18773     },
18774
18775     // private
18776     onInitDrag : function(x, y){
18777         var clone = this.el.dom.cloneNode(true);
18778         clone.id = Roo.id(); // prevent duplicate ids
18779         this.proxy.update(clone);
18780         this.onStartDrag(x, y);
18781         return true;
18782     },
18783
18784     /**
18785      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18786      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18787      */
18788     getProxy : function(){
18789         return this.proxy;  
18790     },
18791
18792     /**
18793      * Hides the drag source's {@link Roo.dd.StatusProxy}
18794      */
18795     hideProxy : function(){
18796         this.proxy.hide();  
18797         this.proxy.reset(true);
18798         this.dragging = false;
18799     },
18800
18801     // private
18802     triggerCacheRefresh : function(){
18803         Roo.dd.DDM.refreshCache(this.groups);
18804     },
18805
18806     // private - override to prevent hiding
18807     b4EndDrag: function(e) {
18808     },
18809
18810     // private - override to prevent moving
18811     endDrag : function(e){
18812         this.onEndDrag(this.dragData, e);
18813     },
18814
18815     // private
18816     onEndDrag : function(data, e){
18817     },
18818     
18819     // private - pin to cursor
18820     autoOffset : function(x, y) {
18821         this.setDelta(-12, -20);
18822     }    
18823 });/*
18824  * Based on:
18825  * Ext JS Library 1.1.1
18826  * Copyright(c) 2006-2007, Ext JS, LLC.
18827  *
18828  * Originally Released Under LGPL - original licence link has changed is not relivant.
18829  *
18830  * Fork - LGPL
18831  * <script type="text/javascript">
18832  */
18833
18834
18835 /**
18836  * @class Roo.dd.DropTarget
18837  * @extends Roo.dd.DDTarget
18838  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18839  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18840  * @constructor
18841  * @param {String/HTMLElement/Element} el The container element
18842  * @param {Object} config
18843  */
18844 Roo.dd.DropTarget = function(el, config){
18845     this.el = Roo.get(el);
18846     
18847     var listeners = false; ;
18848     if (config && config.listeners) {
18849         listeners= config.listeners;
18850         delete config.listeners;
18851     }
18852     Roo.apply(this, config);
18853     
18854     if(this.containerScroll){
18855         Roo.dd.ScrollManager.register(this.el);
18856     }
18857     this.addEvents( {
18858          /**
18859          * @scope Roo.dd.DropTarget
18860          */
18861          
18862          /**
18863          * @event enter
18864          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18865          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18866          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18867          * 
18868          * IMPORTANT : it should set this.overClass and this.dropAllowed
18869          * 
18870          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18871          * @param {Event} e The event
18872          * @param {Object} data An object containing arbitrary data supplied by the drag source
18873          */
18874         "enter" : true,
18875         
18876          /**
18877          * @event over
18878          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18879          * This method will be called on every mouse movement while the drag source is over the drop target.
18880          * This default implementation simply returns the dropAllowed config value.
18881          * 
18882          * IMPORTANT : it should set this.dropAllowed
18883          * 
18884          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18885          * @param {Event} e The event
18886          * @param {Object} data An object containing arbitrary data supplied by the drag source
18887          
18888          */
18889         "over" : true,
18890         /**
18891          * @event out
18892          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18893          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18894          * overClass (if any) from the drop element.
18895          * 
18896          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18897          * @param {Event} e The event
18898          * @param {Object} data An object containing arbitrary data supplied by the drag source
18899          */
18900          "out" : true,
18901          
18902         /**
18903          * @event drop
18904          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18905          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18906          * implementation that does something to process the drop event and returns true so that the drag source's
18907          * repair action does not run.
18908          * 
18909          * IMPORTANT : it should set this.success
18910          * 
18911          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18912          * @param {Event} e The event
18913          * @param {Object} data An object containing arbitrary data supplied by the drag source
18914         */
18915          "drop" : true
18916     });
18917             
18918      
18919     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18920         this.el.dom, 
18921         this.ddGroup || this.group,
18922         {
18923             isTarget: true,
18924             listeners : listeners || {} 
18925            
18926         
18927         }
18928     );
18929
18930 };
18931
18932 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18933     /**
18934      * @cfg {String} overClass
18935      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18936      */
18937      /**
18938      * @cfg {String} ddGroup
18939      * The drag drop group to handle drop events for
18940      */
18941      
18942     /**
18943      * @cfg {String} dropAllowed
18944      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18945      */
18946     dropAllowed : "x-dd-drop-ok",
18947     /**
18948      * @cfg {String} dropNotAllowed
18949      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18950      */
18951     dropNotAllowed : "x-dd-drop-nodrop",
18952     /**
18953      * @cfg {boolean} success
18954      * set this after drop listener.. 
18955      */
18956     success : false,
18957     /**
18958      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18959      * if the drop point is valid for over/enter..
18960      */
18961     valid : false,
18962     // private
18963     isTarget : true,
18964
18965     // private
18966     isNotifyTarget : true,
18967     
18968     /**
18969      * @hide
18970      */
18971     notifyEnter : function(dd, e, data)
18972     {
18973         this.valid = true;
18974         this.fireEvent('enter', dd, e, data);
18975         if(this.overClass){
18976             this.el.addClass(this.overClass);
18977         }
18978         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18979             this.valid ? this.dropAllowed : this.dropNotAllowed
18980         );
18981     },
18982
18983     /**
18984      * @hide
18985      */
18986     notifyOver : function(dd, e, data)
18987     {
18988         this.valid = true;
18989         this.fireEvent('over', dd, e, data);
18990         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18991             this.valid ? this.dropAllowed : this.dropNotAllowed
18992         );
18993     },
18994
18995     /**
18996      * @hide
18997      */
18998     notifyOut : function(dd, e, data)
18999     {
19000         this.fireEvent('out', dd, e, data);
19001         if(this.overClass){
19002             this.el.removeClass(this.overClass);
19003         }
19004     },
19005
19006     /**
19007      * @hide
19008      */
19009     notifyDrop : function(dd, e, data)
19010     {
19011         this.success = false;
19012         this.fireEvent('drop', dd, e, data);
19013         return this.success;
19014     }
19015 });/*
19016  * Based on:
19017  * Ext JS Library 1.1.1
19018  * Copyright(c) 2006-2007, Ext JS, LLC.
19019  *
19020  * Originally Released Under LGPL - original licence link has changed is not relivant.
19021  *
19022  * Fork - LGPL
19023  * <script type="text/javascript">
19024  */
19025
19026
19027 /**
19028  * @class Roo.dd.DragZone
19029  * @extends Roo.dd.DragSource
19030  * This class provides a container DD instance that proxies for multiple child node sources.<br />
19031  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
19032  * @constructor
19033  * @param {String/HTMLElement/Element} el The container element
19034  * @param {Object} config
19035  */
19036 Roo.dd.DragZone = function(el, config){
19037     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
19038     if(this.containerScroll){
19039         Roo.dd.ScrollManager.register(this.el);
19040     }
19041 };
19042
19043 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
19044     /**
19045      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
19046      * for auto scrolling during drag operations.
19047      */
19048     /**
19049      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
19050      * method after a failed drop (defaults to "c3daf9" - light blue)
19051      */
19052
19053     /**
19054      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
19055      * for a valid target to drag based on the mouse down. Override this method
19056      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
19057      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
19058      * @param {EventObject} e The mouse down event
19059      * @return {Object} The dragData
19060      */
19061     getDragData : function(e){
19062         return Roo.dd.Registry.getHandleFromEvent(e);
19063     },
19064     
19065     /**
19066      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
19067      * this.dragData.ddel
19068      * @param {Number} x The x position of the click on the dragged object
19069      * @param {Number} y The y position of the click on the dragged object
19070      * @return {Boolean} true to continue the drag, false to cancel
19071      */
19072     onInitDrag : function(x, y){
19073         this.proxy.update(this.dragData.ddel.cloneNode(true));
19074         this.onStartDrag(x, y);
19075         return true;
19076     },
19077     
19078     /**
19079      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
19080      */
19081     afterRepair : function(){
19082         if(Roo.enableFx){
19083             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
19084         }
19085         this.dragging = false;
19086     },
19087
19088     /**
19089      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
19090      * the XY of this.dragData.ddel
19091      * @param {EventObject} e The mouse up event
19092      * @return {Array} The xy location (e.g. [100, 200])
19093      */
19094     getRepairXY : function(e){
19095         return Roo.Element.fly(this.dragData.ddel).getXY();  
19096     }
19097 });/*
19098  * Based on:
19099  * Ext JS Library 1.1.1
19100  * Copyright(c) 2006-2007, Ext JS, LLC.
19101  *
19102  * Originally Released Under LGPL - original licence link has changed is not relivant.
19103  *
19104  * Fork - LGPL
19105  * <script type="text/javascript">
19106  */
19107 /**
19108  * @class Roo.dd.DropZone
19109  * @extends Roo.dd.DropTarget
19110  * This class provides a container DD instance that proxies for multiple child node targets.<br />
19111  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
19112  * @constructor
19113  * @param {String/HTMLElement/Element} el The container element
19114  * @param {Object} config
19115  */
19116 Roo.dd.DropZone = function(el, config){
19117     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
19118 };
19119
19120 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
19121     /**
19122      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
19123      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
19124      * provide your own custom lookup.
19125      * @param {Event} e The event
19126      * @return {Object} data The custom data
19127      */
19128     getTargetFromEvent : function(e){
19129         return Roo.dd.Registry.getTargetFromEvent(e);
19130     },
19131
19132     /**
19133      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
19134      * that it has registered.  This method has no default implementation and should be overridden to provide
19135      * node-specific processing if necessary.
19136      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
19137      * {@link #getTargetFromEvent} for this node)
19138      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19139      * @param {Event} e The event
19140      * @param {Object} data An object containing arbitrary data supplied by the drag source
19141      */
19142     onNodeEnter : function(n, dd, e, data){
19143         
19144     },
19145
19146     /**
19147      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
19148      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
19149      * overridden to provide the proper feedback.
19150      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19151      * {@link #getTargetFromEvent} for this node)
19152      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19153      * @param {Event} e The event
19154      * @param {Object} data An object containing arbitrary data supplied by the drag source
19155      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19156      * underlying {@link Roo.dd.StatusProxy} can be updated
19157      */
19158     onNodeOver : function(n, dd, e, data){
19159         return this.dropAllowed;
19160     },
19161
19162     /**
19163      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
19164      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
19165      * node-specific processing if necessary.
19166      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19167      * {@link #getTargetFromEvent} for this node)
19168      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19169      * @param {Event} e The event
19170      * @param {Object} data An object containing arbitrary data supplied by the drag source
19171      */
19172     onNodeOut : function(n, dd, e, data){
19173         
19174     },
19175
19176     /**
19177      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
19178      * the drop node.  The default implementation returns false, so it should be overridden to provide the
19179      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
19180      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19181      * {@link #getTargetFromEvent} for this node)
19182      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19183      * @param {Event} e The event
19184      * @param {Object} data An object containing arbitrary data supplied by the drag source
19185      * @return {Boolean} True if the drop was valid, else false
19186      */
19187     onNodeDrop : function(n, dd, e, data){
19188         return false;
19189     },
19190
19191     /**
19192      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
19193      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
19194      * it should be overridden to provide the proper feedback if necessary.
19195      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19196      * @param {Event} e The event
19197      * @param {Object} data An object containing arbitrary data supplied by the drag source
19198      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19199      * underlying {@link Roo.dd.StatusProxy} can be updated
19200      */
19201     onContainerOver : function(dd, e, data){
19202         return this.dropNotAllowed;
19203     },
19204
19205     /**
19206      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
19207      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
19208      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
19209      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
19210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19211      * @param {Event} e The event
19212      * @param {Object} data An object containing arbitrary data supplied by the drag source
19213      * @return {Boolean} True if the drop was valid, else false
19214      */
19215     onContainerDrop : function(dd, e, data){
19216         return false;
19217     },
19218
19219     /**
19220      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
19221      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
19222      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
19223      * you should override this method and provide a custom implementation.
19224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19225      * @param {Event} e The event
19226      * @param {Object} data An object containing arbitrary data supplied by the drag source
19227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19228      * underlying {@link Roo.dd.StatusProxy} can be updated
19229      */
19230     notifyEnter : function(dd, e, data){
19231         return this.dropNotAllowed;
19232     },
19233
19234     /**
19235      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
19236      * This method will be called on every mouse movement while the drag source is over the drop zone.
19237      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
19238      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
19239      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
19240      * registered node, it will call {@link #onContainerOver}.
19241      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19242      * @param {Event} e The event
19243      * @param {Object} data An object containing arbitrary data supplied by the drag source
19244      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19245      * underlying {@link Roo.dd.StatusProxy} can be updated
19246      */
19247     notifyOver : function(dd, e, data){
19248         var n = this.getTargetFromEvent(e);
19249         if(!n){ // not over valid drop target
19250             if(this.lastOverNode){
19251                 this.onNodeOut(this.lastOverNode, dd, e, data);
19252                 this.lastOverNode = null;
19253             }
19254             return this.onContainerOver(dd, e, data);
19255         }
19256         if(this.lastOverNode != n){
19257             if(this.lastOverNode){
19258                 this.onNodeOut(this.lastOverNode, dd, e, data);
19259             }
19260             this.onNodeEnter(n, dd, e, data);
19261             this.lastOverNode = n;
19262         }
19263         return this.onNodeOver(n, dd, e, data);
19264     },
19265
19266     /**
19267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
19268      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
19269      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
19270      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
19271      * @param {Event} e The event
19272      * @param {Object} data An object containing arbitrary data supplied by the drag zone
19273      */
19274     notifyOut : function(dd, e, data){
19275         if(this.lastOverNode){
19276             this.onNodeOut(this.lastOverNode, dd, e, data);
19277             this.lastOverNode = null;
19278         }
19279     },
19280
19281     /**
19282      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
19283      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
19284      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
19285      * otherwise it will call {@link #onContainerDrop}.
19286      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19287      * @param {Event} e The event
19288      * @param {Object} data An object containing arbitrary data supplied by the drag source
19289      * @return {Boolean} True if the drop was valid, else false
19290      */
19291     notifyDrop : function(dd, e, data){
19292         if(this.lastOverNode){
19293             this.onNodeOut(this.lastOverNode, dd, e, data);
19294             this.lastOverNode = null;
19295         }
19296         var n = this.getTargetFromEvent(e);
19297         return n ?
19298             this.onNodeDrop(n, dd, e, data) :
19299             this.onContainerDrop(dd, e, data);
19300     },
19301
19302     // private
19303     triggerCacheRefresh : function(){
19304         Roo.dd.DDM.refreshCache(this.groups);
19305     }  
19306 });/*
19307  * Based on:
19308  * Ext JS Library 1.1.1
19309  * Copyright(c) 2006-2007, Ext JS, LLC.
19310  *
19311  * Originally Released Under LGPL - original licence link has changed is not relivant.
19312  *
19313  * Fork - LGPL
19314  * <script type="text/javascript">
19315  */
19316
19317
19318 /**
19319  * @class Roo.data.SortTypes
19320  * @singleton
19321  * Defines the default sorting (casting?) comparison functions used when sorting data.
19322  */
19323 Roo.data.SortTypes = {
19324     /**
19325      * Default sort that does nothing
19326      * @param {Mixed} s The value being converted
19327      * @return {Mixed} The comparison value
19328      */
19329     none : function(s){
19330         return s;
19331     },
19332     
19333     /**
19334      * The regular expression used to strip tags
19335      * @type {RegExp}
19336      * @property
19337      */
19338     stripTagsRE : /<\/?[^>]+>/gi,
19339     
19340     /**
19341      * Strips all HTML tags to sort on text only
19342      * @param {Mixed} s The value being converted
19343      * @return {String} The comparison value
19344      */
19345     asText : function(s){
19346         return String(s).replace(this.stripTagsRE, "");
19347     },
19348     
19349     /**
19350      * Strips all HTML tags to sort on text only - Case insensitive
19351      * @param {Mixed} s The value being converted
19352      * @return {String} The comparison value
19353      */
19354     asUCText : function(s){
19355         return String(s).toUpperCase().replace(this.stripTagsRE, "");
19356     },
19357     
19358     /**
19359      * Case insensitive string
19360      * @param {Mixed} s The value being converted
19361      * @return {String} The comparison value
19362      */
19363     asUCString : function(s) {
19364         return String(s).toUpperCase();
19365     },
19366     
19367     /**
19368      * Date sorting
19369      * @param {Mixed} s The value being converted
19370      * @return {Number} The comparison value
19371      */
19372     asDate : function(s) {
19373         if(!s){
19374             return 0;
19375         }
19376         if(s instanceof Date){
19377             return s.getTime();
19378         }
19379         return Date.parse(String(s));
19380     },
19381     
19382     /**
19383      * Float sorting
19384      * @param {Mixed} s The value being converted
19385      * @return {Float} The comparison value
19386      */
19387     asFloat : function(s) {
19388         var val = parseFloat(String(s).replace(/,/g, ""));
19389         if(isNaN(val)) val = 0;
19390         return val;
19391     },
19392     
19393     /**
19394      * Integer sorting
19395      * @param {Mixed} s The value being converted
19396      * @return {Number} The comparison value
19397      */
19398     asInt : function(s) {
19399         var val = parseInt(String(s).replace(/,/g, ""));
19400         if(isNaN(val)) val = 0;
19401         return val;
19402     }
19403 };/*
19404  * Based on:
19405  * Ext JS Library 1.1.1
19406  * Copyright(c) 2006-2007, Ext JS, LLC.
19407  *
19408  * Originally Released Under LGPL - original licence link has changed is not relivant.
19409  *
19410  * Fork - LGPL
19411  * <script type="text/javascript">
19412  */
19413
19414 /**
19415 * @class Roo.data.Record
19416  * Instances of this class encapsulate both record <em>definition</em> information, and record
19417  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
19418  * to access Records cached in an {@link Roo.data.Store} object.<br>
19419  * <p>
19420  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
19421  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
19422  * objects.<br>
19423  * <p>
19424  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
19425  * @constructor
19426  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
19427  * {@link #create}. The parameters are the same.
19428  * @param {Array} data An associative Array of data values keyed by the field name.
19429  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
19430  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
19431  * not specified an integer id is generated.
19432  */
19433 Roo.data.Record = function(data, id){
19434     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
19435     this.data = data;
19436 };
19437
19438 /**
19439  * Generate a constructor for a specific record layout.
19440  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
19441  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
19442  * Each field definition object may contain the following properties: <ul>
19443  * <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,
19444  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
19445  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
19446  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
19447  * is being used, then this is a string containing the javascript expression to reference the data relative to 
19448  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
19449  * to the data item relative to the record element. If the mapping expression is the same as the field name,
19450  * this may be omitted.</p></li>
19451  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
19452  * <ul><li>auto (Default, implies no conversion)</li>
19453  * <li>string</li>
19454  * <li>int</li>
19455  * <li>float</li>
19456  * <li>boolean</li>
19457  * <li>date</li></ul></p></li>
19458  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
19459  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19460  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19461  * by the Reader into an object that will be stored in the Record. It is passed the
19462  * following parameters:<ul>
19463  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19464  * </ul></p></li>
19465  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19466  * </ul>
19467  * <br>usage:<br><pre><code>
19468 var TopicRecord = Roo.data.Record.create(
19469     {name: 'title', mapping: 'topic_title'},
19470     {name: 'author', mapping: 'username'},
19471     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19472     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19473     {name: 'lastPoster', mapping: 'user2'},
19474     {name: 'excerpt', mapping: 'post_text'}
19475 );
19476
19477 var myNewRecord = new TopicRecord({
19478     title: 'Do my job please',
19479     author: 'noobie',
19480     totalPosts: 1,
19481     lastPost: new Date(),
19482     lastPoster: 'Animal',
19483     excerpt: 'No way dude!'
19484 });
19485 myStore.add(myNewRecord);
19486 </code></pre>
19487  * @method create
19488  * @static
19489  */
19490 Roo.data.Record.create = function(o){
19491     var f = function(){
19492         f.superclass.constructor.apply(this, arguments);
19493     };
19494     Roo.extend(f, Roo.data.Record);
19495     var p = f.prototype;
19496     p.fields = new Roo.util.MixedCollection(false, function(field){
19497         return field.name;
19498     });
19499     for(var i = 0, len = o.length; i < len; i++){
19500         p.fields.add(new Roo.data.Field(o[i]));
19501     }
19502     f.getField = function(name){
19503         return p.fields.get(name);  
19504     };
19505     return f;
19506 };
19507
19508 Roo.data.Record.AUTO_ID = 1000;
19509 Roo.data.Record.EDIT = 'edit';
19510 Roo.data.Record.REJECT = 'reject';
19511 Roo.data.Record.COMMIT = 'commit';
19512
19513 Roo.data.Record.prototype = {
19514     /**
19515      * Readonly flag - true if this record has been modified.
19516      * @type Boolean
19517      */
19518     dirty : false,
19519     editing : false,
19520     error: null,
19521     modified: null,
19522
19523     // private
19524     join : function(store){
19525         this.store = store;
19526     },
19527
19528     /**
19529      * Set the named field to the specified value.
19530      * @param {String} name The name of the field to set.
19531      * @param {Object} value The value to set the field to.
19532      */
19533     set : function(name, value){
19534         if(this.data[name] == value){
19535             return;
19536         }
19537         this.dirty = true;
19538         if(!this.modified){
19539             this.modified = {};
19540         }
19541         if(typeof this.modified[name] == 'undefined'){
19542             this.modified[name] = this.data[name];
19543         }
19544         this.data[name] = value;
19545         if(!this.editing && this.store){
19546             this.store.afterEdit(this);
19547         }       
19548     },
19549
19550     /**
19551      * Get the value of the named field.
19552      * @param {String} name The name of the field to get the value of.
19553      * @return {Object} The value of the field.
19554      */
19555     get : function(name){
19556         return this.data[name]; 
19557     },
19558
19559     // private
19560     beginEdit : function(){
19561         this.editing = true;
19562         this.modified = {}; 
19563     },
19564
19565     // private
19566     cancelEdit : function(){
19567         this.editing = false;
19568         delete this.modified;
19569     },
19570
19571     // private
19572     endEdit : function(){
19573         this.editing = false;
19574         if(this.dirty && this.store){
19575             this.store.afterEdit(this);
19576         }
19577     },
19578
19579     /**
19580      * Usually called by the {@link Roo.data.Store} which owns the Record.
19581      * Rejects all changes made to the Record since either creation, or the last commit operation.
19582      * Modified fields are reverted to their original values.
19583      * <p>
19584      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19585      * of reject operations.
19586      */
19587     reject : function(){
19588         var m = this.modified;
19589         for(var n in m){
19590             if(typeof m[n] != "function"){
19591                 this.data[n] = m[n];
19592             }
19593         }
19594         this.dirty = false;
19595         delete this.modified;
19596         this.editing = false;
19597         if(this.store){
19598             this.store.afterReject(this);
19599         }
19600     },
19601
19602     /**
19603      * Usually called by the {@link Roo.data.Store} which owns the Record.
19604      * Commits all changes made to the Record since either creation, or the last commit operation.
19605      * <p>
19606      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19607      * of commit operations.
19608      */
19609     commit : function(){
19610         this.dirty = false;
19611         delete this.modified;
19612         this.editing = false;
19613         if(this.store){
19614             this.store.afterCommit(this);
19615         }
19616     },
19617
19618     // private
19619     hasError : function(){
19620         return this.error != null;
19621     },
19622
19623     // private
19624     clearError : function(){
19625         this.error = null;
19626     },
19627
19628     /**
19629      * Creates a copy of this record.
19630      * @param {String} id (optional) A new record id if you don't want to use this record's id
19631      * @return {Record}
19632      */
19633     copy : function(newId) {
19634         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19635     }
19636 };/*
19637  * Based on:
19638  * Ext JS Library 1.1.1
19639  * Copyright(c) 2006-2007, Ext JS, LLC.
19640  *
19641  * Originally Released Under LGPL - original licence link has changed is not relivant.
19642  *
19643  * Fork - LGPL
19644  * <script type="text/javascript">
19645  */
19646
19647
19648
19649 /**
19650  * @class Roo.data.Store
19651  * @extends Roo.util.Observable
19652  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19653  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19654  * <p>
19655  * 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
19656  * has no knowledge of the format of the data returned by the Proxy.<br>
19657  * <p>
19658  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19659  * instances from the data object. These records are cached and made available through accessor functions.
19660  * @constructor
19661  * Creates a new Store.
19662  * @param {Object} config A config object containing the objects needed for the Store to access data,
19663  * and read the data into Records.
19664  */
19665 Roo.data.Store = function(config){
19666     this.data = new Roo.util.MixedCollection(false);
19667     this.data.getKey = function(o){
19668         return o.id;
19669     };
19670     this.baseParams = {};
19671     // private
19672     this.paramNames = {
19673         "start" : "start",
19674         "limit" : "limit",
19675         "sort" : "sort",
19676         "dir" : "dir",
19677         "multisort" : "_multisort"
19678     };
19679
19680     if(config && config.data){
19681         this.inlineData = config.data;
19682         delete config.data;
19683     }
19684
19685     Roo.apply(this, config);
19686     
19687     if(this.reader){ // reader passed
19688         this.reader = Roo.factory(this.reader, Roo.data);
19689         this.reader.xmodule = this.xmodule || false;
19690         if(!this.recordType){
19691             this.recordType = this.reader.recordType;
19692         }
19693         if(this.reader.onMetaChange){
19694             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19695         }
19696     }
19697
19698     if(this.recordType){
19699         this.fields = this.recordType.prototype.fields;
19700     }
19701     this.modified = [];
19702
19703     this.addEvents({
19704         /**
19705          * @event datachanged
19706          * Fires when the data cache has changed, and a widget which is using this Store
19707          * as a Record cache should refresh its view.
19708          * @param {Store} this
19709          */
19710         datachanged : true,
19711         /**
19712          * @event metachange
19713          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19714          * @param {Store} this
19715          * @param {Object} meta The JSON metadata
19716          */
19717         metachange : true,
19718         /**
19719          * @event add
19720          * Fires when Records have been added to the Store
19721          * @param {Store} this
19722          * @param {Roo.data.Record[]} records The array of Records added
19723          * @param {Number} index The index at which the record(s) were added
19724          */
19725         add : true,
19726         /**
19727          * @event remove
19728          * Fires when a Record has been removed from the Store
19729          * @param {Store} this
19730          * @param {Roo.data.Record} record The Record that was removed
19731          * @param {Number} index The index at which the record was removed
19732          */
19733         remove : true,
19734         /**
19735          * @event update
19736          * Fires when a Record has been updated
19737          * @param {Store} this
19738          * @param {Roo.data.Record} record The Record that was updated
19739          * @param {String} operation The update operation being performed.  Value may be one of:
19740          * <pre><code>
19741  Roo.data.Record.EDIT
19742  Roo.data.Record.REJECT
19743  Roo.data.Record.COMMIT
19744          * </code></pre>
19745          */
19746         update : true,
19747         /**
19748          * @event clear
19749          * Fires when the data cache has been cleared.
19750          * @param {Store} this
19751          */
19752         clear : true,
19753         /**
19754          * @event beforeload
19755          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19756          * the load action will be canceled.
19757          * @param {Store} this
19758          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19759          */
19760         beforeload : true,
19761         /**
19762          * @event beforeloadadd
19763          * Fires after a new set of Records has been loaded.
19764          * @param {Store} this
19765          * @param {Roo.data.Record[]} records The Records that were loaded
19766          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19767          */
19768         beforeloadadd : true,
19769         /**
19770          * @event load
19771          * Fires after a new set of Records has been loaded, before they are added to the store.
19772          * @param {Store} this
19773          * @param {Roo.data.Record[]} records The Records that were loaded
19774          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19775          * @params {Object} return from reader
19776          */
19777         load : true,
19778         /**
19779          * @event loadexception
19780          * Fires if an exception occurs in the Proxy during loading.
19781          * Called with the signature of the Proxy's "loadexception" event.
19782          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19783          * 
19784          * @param {Proxy} 
19785          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19786          * @param {Object} load options 
19787          * @param {Object} jsonData from your request (normally this contains the Exception)
19788          */
19789         loadexception : true
19790     });
19791     
19792     if(this.proxy){
19793         this.proxy = Roo.factory(this.proxy, Roo.data);
19794         this.proxy.xmodule = this.xmodule || false;
19795         this.relayEvents(this.proxy,  ["loadexception"]);
19796     }
19797     this.sortToggle = {};
19798     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19799
19800     Roo.data.Store.superclass.constructor.call(this);
19801
19802     if(this.inlineData){
19803         this.loadData(this.inlineData);
19804         delete this.inlineData;
19805     }
19806 };
19807
19808 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19809      /**
19810     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19811     * without a remote query - used by combo/forms at present.
19812     */
19813     
19814     /**
19815     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19816     */
19817     /**
19818     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19819     */
19820     /**
19821     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19822     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19823     */
19824     /**
19825     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19826     * on any HTTP request
19827     */
19828     /**
19829     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19830     */
19831     /**
19832     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19833     */
19834     multiSort: false,
19835     /**
19836     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19837     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19838     */
19839     remoteSort : false,
19840
19841     /**
19842     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19843      * loaded or when a record is removed. (defaults to false).
19844     */
19845     pruneModifiedRecords : false,
19846
19847     // private
19848     lastOptions : null,
19849
19850     /**
19851      * Add Records to the Store and fires the add event.
19852      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19853      */
19854     add : function(records){
19855         records = [].concat(records);
19856         for(var i = 0, len = records.length; i < len; i++){
19857             records[i].join(this);
19858         }
19859         var index = this.data.length;
19860         this.data.addAll(records);
19861         this.fireEvent("add", this, records, index);
19862     },
19863
19864     /**
19865      * Remove a Record from the Store and fires the remove event.
19866      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19867      */
19868     remove : function(record){
19869         var index = this.data.indexOf(record);
19870         this.data.removeAt(index);
19871         if(this.pruneModifiedRecords){
19872             this.modified.remove(record);
19873         }
19874         this.fireEvent("remove", this, record, index);
19875     },
19876
19877     /**
19878      * Remove all Records from the Store and fires the clear event.
19879      */
19880     removeAll : function(){
19881         this.data.clear();
19882         if(this.pruneModifiedRecords){
19883             this.modified = [];
19884         }
19885         this.fireEvent("clear", this);
19886     },
19887
19888     /**
19889      * Inserts Records to the Store at the given index and fires the add event.
19890      * @param {Number} index The start index at which to insert the passed Records.
19891      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19892      */
19893     insert : function(index, records){
19894         records = [].concat(records);
19895         for(var i = 0, len = records.length; i < len; i++){
19896             this.data.insert(index, records[i]);
19897             records[i].join(this);
19898         }
19899         this.fireEvent("add", this, records, index);
19900     },
19901
19902     /**
19903      * Get the index within the cache of the passed Record.
19904      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19905      * @return {Number} The index of the passed Record. Returns -1 if not found.
19906      */
19907     indexOf : function(record){
19908         return this.data.indexOf(record);
19909     },
19910
19911     /**
19912      * Get the index within the cache of the Record with the passed id.
19913      * @param {String} id The id of the Record to find.
19914      * @return {Number} The index of the Record. Returns -1 if not found.
19915      */
19916     indexOfId : function(id){
19917         return this.data.indexOfKey(id);
19918     },
19919
19920     /**
19921      * Get the Record with the specified id.
19922      * @param {String} id The id of the Record to find.
19923      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19924      */
19925     getById : function(id){
19926         return this.data.key(id);
19927     },
19928
19929     /**
19930      * Get the Record at the specified index.
19931      * @param {Number} index The index of the Record to find.
19932      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19933      */
19934     getAt : function(index){
19935         return this.data.itemAt(index);
19936     },
19937
19938     /**
19939      * Returns a range of Records between specified indices.
19940      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19941      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19942      * @return {Roo.data.Record[]} An array of Records
19943      */
19944     getRange : function(start, end){
19945         return this.data.getRange(start, end);
19946     },
19947
19948     // private
19949     storeOptions : function(o){
19950         o = Roo.apply({}, o);
19951         delete o.callback;
19952         delete o.scope;
19953         this.lastOptions = o;
19954     },
19955
19956     /**
19957      * Loads the Record cache from the configured Proxy using the configured Reader.
19958      * <p>
19959      * If using remote paging, then the first load call must specify the <em>start</em>
19960      * and <em>limit</em> properties in the options.params property to establish the initial
19961      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19962      * <p>
19963      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19964      * and this call will return before the new data has been loaded. Perform any post-processing
19965      * in a callback function, or in a "load" event handler.</strong>
19966      * <p>
19967      * @param {Object} options An object containing properties which control loading options:<ul>
19968      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19969      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19970      * passed the following arguments:<ul>
19971      * <li>r : Roo.data.Record[]</li>
19972      * <li>options: Options object from the load call</li>
19973      * <li>success: Boolean success indicator</li></ul></li>
19974      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19975      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19976      * </ul>
19977      */
19978     load : function(options){
19979         options = options || {};
19980         if(this.fireEvent("beforeload", this, options) !== false){
19981             this.storeOptions(options);
19982             var p = Roo.apply(options.params || {}, this.baseParams);
19983             // if meta was not loaded from remote source.. try requesting it.
19984             if (!this.reader.metaFromRemote) {
19985                 p._requestMeta = 1;
19986             }
19987             if(this.sortInfo && this.remoteSort){
19988                 var pn = this.paramNames;
19989                 p[pn["sort"]] = this.sortInfo.field;
19990                 p[pn["dir"]] = this.sortInfo.direction;
19991             }
19992             if (this.multiSort) {
19993                 var pn = this.paramNames;
19994                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19995             }
19996             
19997             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19998         }
19999     },
20000
20001     /**
20002      * Reloads the Record cache from the configured Proxy using the configured Reader and
20003      * the options from the last load operation performed.
20004      * @param {Object} options (optional) An object containing properties which may override the options
20005      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
20006      * the most recently used options are reused).
20007      */
20008     reload : function(options){
20009         this.load(Roo.applyIf(options||{}, this.lastOptions));
20010     },
20011
20012     // private
20013     // Called as a callback by the Reader during a load operation.
20014     loadRecords : function(o, options, success){
20015         if(!o || success === false){
20016             if(success !== false){
20017                 this.fireEvent("load", this, [], options, o);
20018             }
20019             if(options.callback){
20020                 options.callback.call(options.scope || this, [], options, false);
20021             }
20022             return;
20023         }
20024         // if data returned failure - throw an exception.
20025         if (o.success === false) {
20026             // show a message if no listener is registered.
20027             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
20028                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
20029             }
20030             // loadmask wil be hooked into this..
20031             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
20032             return;
20033         }
20034         var r = o.records, t = o.totalRecords || r.length;
20035         
20036         this.fireEvent("beforeloadadd", this, r, options, o);
20037         
20038         if(!options || options.add !== true){
20039             if(this.pruneModifiedRecords){
20040                 this.modified = [];
20041             }
20042             for(var i = 0, len = r.length; i < len; i++){
20043                 r[i].join(this);
20044             }
20045             if(this.snapshot){
20046                 this.data = this.snapshot;
20047                 delete this.snapshot;
20048             }
20049             this.data.clear();
20050             this.data.addAll(r);
20051             this.totalLength = t;
20052             this.applySort();
20053             this.fireEvent("datachanged", this);
20054         }else{
20055             this.totalLength = Math.max(t, this.data.length+r.length);
20056             this.add(r);
20057         }
20058         this.fireEvent("load", this, r, options, o);
20059         if(options.callback){
20060             options.callback.call(options.scope || this, r, options, true);
20061         }
20062     },
20063
20064
20065     /**
20066      * Loads data from a passed data block. A Reader which understands the format of the data
20067      * must have been configured in the constructor.
20068      * @param {Object} data The data block from which to read the Records.  The format of the data expected
20069      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
20070      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
20071      */
20072     loadData : function(o, append){
20073         var r = this.reader.readRecords(o);
20074         this.loadRecords(r, {add: append}, true);
20075     },
20076
20077     /**
20078      * Gets the number of cached records.
20079      * <p>
20080      * <em>If using paging, this may not be the total size of the dataset. If the data object
20081      * used by the Reader contains the dataset size, then the getTotalCount() function returns
20082      * the data set size</em>
20083      */
20084     getCount : function(){
20085         return this.data.length || 0;
20086     },
20087
20088     /**
20089      * Gets the total number of records in the dataset as returned by the server.
20090      * <p>
20091      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
20092      * the dataset size</em>
20093      */
20094     getTotalCount : function(){
20095         return this.totalLength || 0;
20096     },
20097
20098     /**
20099      * Returns the sort state of the Store as an object with two properties:
20100      * <pre><code>
20101  field {String} The name of the field by which the Records are sorted
20102  direction {String} The sort order, "ASC" or "DESC"
20103      * </code></pre>
20104      */
20105     getSortState : function(){
20106         return this.sortInfo;
20107     },
20108
20109     // private
20110     applySort : function(){
20111         if(this.sortInfo && !this.remoteSort){
20112             var s = this.sortInfo, f = s.field;
20113             var st = this.fields.get(f).sortType;
20114             var fn = function(r1, r2){
20115                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
20116                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
20117             };
20118             this.data.sort(s.direction, fn);
20119             if(this.snapshot && this.snapshot != this.data){
20120                 this.snapshot.sort(s.direction, fn);
20121             }
20122         }
20123     },
20124
20125     /**
20126      * Sets the default sort column and order to be used by the next load operation.
20127      * @param {String} fieldName The name of the field to sort by.
20128      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20129      */
20130     setDefaultSort : function(field, dir){
20131         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
20132     },
20133
20134     /**
20135      * Sort the Records.
20136      * If remote sorting is used, the sort is performed on the server, and the cache is
20137      * reloaded. If local sorting is used, the cache is sorted internally.
20138      * @param {String} fieldName The name of the field to sort by.
20139      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20140      */
20141     sort : function(fieldName, dir){
20142         var f = this.fields.get(fieldName);
20143         if(!dir){
20144             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
20145             
20146             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
20147                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
20148             }else{
20149                 dir = f.sortDir;
20150             }
20151         }
20152         this.sortToggle[f.name] = dir;
20153         this.sortInfo = {field: f.name, direction: dir};
20154         if(!this.remoteSort){
20155             this.applySort();
20156             this.fireEvent("datachanged", this);
20157         }else{
20158             this.load(this.lastOptions);
20159         }
20160     },
20161
20162     /**
20163      * Calls the specified function for each of the Records in the cache.
20164      * @param {Function} fn The function to call. The Record is passed as the first parameter.
20165      * Returning <em>false</em> aborts and exits the iteration.
20166      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
20167      */
20168     each : function(fn, scope){
20169         this.data.each(fn, scope);
20170     },
20171
20172     /**
20173      * Gets all records modified since the last commit.  Modified records are persisted across load operations
20174      * (e.g., during paging).
20175      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
20176      */
20177     getModifiedRecords : function(){
20178         return this.modified;
20179     },
20180
20181     // private
20182     createFilterFn : function(property, value, anyMatch){
20183         if(!value.exec){ // not a regex
20184             value = String(value);
20185             if(value.length == 0){
20186                 return false;
20187             }
20188             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
20189         }
20190         return function(r){
20191             return value.test(r.data[property]);
20192         };
20193     },
20194
20195     /**
20196      * Sums the value of <i>property</i> for each record between start and end and returns the result.
20197      * @param {String} property A field on your records
20198      * @param {Number} start The record index to start at (defaults to 0)
20199      * @param {Number} end The last record index to include (defaults to length - 1)
20200      * @return {Number} The sum
20201      */
20202     sum : function(property, start, end){
20203         var rs = this.data.items, v = 0;
20204         start = start || 0;
20205         end = (end || end === 0) ? end : rs.length-1;
20206
20207         for(var i = start; i <= end; i++){
20208             v += (rs[i].data[property] || 0);
20209         }
20210         return v;
20211     },
20212
20213     /**
20214      * Filter the records by a specified property.
20215      * @param {String} field A field on your records
20216      * @param {String/RegExp} value Either a string that the field
20217      * should start with or a RegExp to test against the field
20218      * @param {Boolean} anyMatch True to match any part not just the beginning
20219      */
20220     filter : function(property, value, anyMatch){
20221         var fn = this.createFilterFn(property, value, anyMatch);
20222         return fn ? this.filterBy(fn) : this.clearFilter();
20223     },
20224
20225     /**
20226      * Filter by a function. The specified function will be called with each
20227      * record in this data source. If the function returns true the record is included,
20228      * otherwise it is filtered.
20229      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20230      * @param {Object} scope (optional) The scope of the function (defaults to this)
20231      */
20232     filterBy : function(fn, scope){
20233         this.snapshot = this.snapshot || this.data;
20234         this.data = this.queryBy(fn, scope||this);
20235         this.fireEvent("datachanged", this);
20236     },
20237
20238     /**
20239      * Query the records by a specified property.
20240      * @param {String} field A field on your records
20241      * @param {String/RegExp} value Either a string that the field
20242      * should start with or a RegExp to test against the field
20243      * @param {Boolean} anyMatch True to match any part not just the beginning
20244      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20245      */
20246     query : function(property, value, anyMatch){
20247         var fn = this.createFilterFn(property, value, anyMatch);
20248         return fn ? this.queryBy(fn) : this.data.clone();
20249     },
20250
20251     /**
20252      * Query by a function. The specified function will be called with each
20253      * record in this data source. If the function returns true the record is included
20254      * in the results.
20255      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20256      * @param {Object} scope (optional) The scope of the function (defaults to this)
20257       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20258      **/
20259     queryBy : function(fn, scope){
20260         var data = this.snapshot || this.data;
20261         return data.filterBy(fn, scope||this);
20262     },
20263
20264     /**
20265      * Collects unique values for a particular dataIndex from this store.
20266      * @param {String} dataIndex The property to collect
20267      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
20268      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
20269      * @return {Array} An array of the unique values
20270      **/
20271     collect : function(dataIndex, allowNull, bypassFilter){
20272         var d = (bypassFilter === true && this.snapshot) ?
20273                 this.snapshot.items : this.data.items;
20274         var v, sv, r = [], l = {};
20275         for(var i = 0, len = d.length; i < len; i++){
20276             v = d[i].data[dataIndex];
20277             sv = String(v);
20278             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
20279                 l[sv] = true;
20280                 r[r.length] = v;
20281             }
20282         }
20283         return r;
20284     },
20285
20286     /**
20287      * Revert to a view of the Record cache with no filtering applied.
20288      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
20289      */
20290     clearFilter : function(suppressEvent){
20291         if(this.snapshot && this.snapshot != this.data){
20292             this.data = this.snapshot;
20293             delete this.snapshot;
20294             if(suppressEvent !== true){
20295                 this.fireEvent("datachanged", this);
20296             }
20297         }
20298     },
20299
20300     // private
20301     afterEdit : function(record){
20302         if(this.modified.indexOf(record) == -1){
20303             this.modified.push(record);
20304         }
20305         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
20306     },
20307     
20308     // private
20309     afterReject : function(record){
20310         this.modified.remove(record);
20311         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
20312     },
20313
20314     // private
20315     afterCommit : function(record){
20316         this.modified.remove(record);
20317         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
20318     },
20319
20320     /**
20321      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
20322      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
20323      */
20324     commitChanges : function(){
20325         var m = this.modified.slice(0);
20326         this.modified = [];
20327         for(var i = 0, len = m.length; i < len; i++){
20328             m[i].commit();
20329         }
20330     },
20331
20332     /**
20333      * Cancel outstanding changes on all changed records.
20334      */
20335     rejectChanges : function(){
20336         var m = this.modified.slice(0);
20337         this.modified = [];
20338         for(var i = 0, len = m.length; i < len; i++){
20339             m[i].reject();
20340         }
20341     },
20342
20343     onMetaChange : function(meta, rtype, o){
20344         this.recordType = rtype;
20345         this.fields = rtype.prototype.fields;
20346         delete this.snapshot;
20347         this.sortInfo = meta.sortInfo || this.sortInfo;
20348         this.modified = [];
20349         this.fireEvent('metachange', this, this.reader.meta);
20350     }
20351 });/*
20352  * Based on:
20353  * Ext JS Library 1.1.1
20354  * Copyright(c) 2006-2007, Ext JS, LLC.
20355  *
20356  * Originally Released Under LGPL - original licence link has changed is not relivant.
20357  *
20358  * Fork - LGPL
20359  * <script type="text/javascript">
20360  */
20361
20362 /**
20363  * @class Roo.data.SimpleStore
20364  * @extends Roo.data.Store
20365  * Small helper class to make creating Stores from Array data easier.
20366  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
20367  * @cfg {Array} fields An array of field definition objects, or field name strings.
20368  * @cfg {Array} data The multi-dimensional array of data
20369  * @constructor
20370  * @param {Object} config
20371  */
20372 Roo.data.SimpleStore = function(config){
20373     Roo.data.SimpleStore.superclass.constructor.call(this, {
20374         isLocal : true,
20375         reader: new Roo.data.ArrayReader({
20376                 id: config.id
20377             },
20378             Roo.data.Record.create(config.fields)
20379         ),
20380         proxy : new Roo.data.MemoryProxy(config.data)
20381     });
20382     this.load();
20383 };
20384 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
20385  * Based on:
20386  * Ext JS Library 1.1.1
20387  * Copyright(c) 2006-2007, Ext JS, LLC.
20388  *
20389  * Originally Released Under LGPL - original licence link has changed is not relivant.
20390  *
20391  * Fork - LGPL
20392  * <script type="text/javascript">
20393  */
20394
20395 /**
20396 /**
20397  * @extends Roo.data.Store
20398  * @class Roo.data.JsonStore
20399  * Small helper class to make creating Stores for JSON data easier. <br/>
20400 <pre><code>
20401 var store = new Roo.data.JsonStore({
20402     url: 'get-images.php',
20403     root: 'images',
20404     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
20405 });
20406 </code></pre>
20407  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
20408  * JsonReader and HttpProxy (unless inline data is provided).</b>
20409  * @cfg {Array} fields An array of field definition objects, or field name strings.
20410  * @constructor
20411  * @param {Object} config
20412  */
20413 Roo.data.JsonStore = function(c){
20414     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
20415         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
20416         reader: new Roo.data.JsonReader(c, c.fields)
20417     }));
20418 };
20419 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
20420  * Based on:
20421  * Ext JS Library 1.1.1
20422  * Copyright(c) 2006-2007, Ext JS, LLC.
20423  *
20424  * Originally Released Under LGPL - original licence link has changed is not relivant.
20425  *
20426  * Fork - LGPL
20427  * <script type="text/javascript">
20428  */
20429
20430  
20431 Roo.data.Field = function(config){
20432     if(typeof config == "string"){
20433         config = {name: config};
20434     }
20435     Roo.apply(this, config);
20436     
20437     if(!this.type){
20438         this.type = "auto";
20439     }
20440     
20441     var st = Roo.data.SortTypes;
20442     // named sortTypes are supported, here we look them up
20443     if(typeof this.sortType == "string"){
20444         this.sortType = st[this.sortType];
20445     }
20446     
20447     // set default sortType for strings and dates
20448     if(!this.sortType){
20449         switch(this.type){
20450             case "string":
20451                 this.sortType = st.asUCString;
20452                 break;
20453             case "date":
20454                 this.sortType = st.asDate;
20455                 break;
20456             default:
20457                 this.sortType = st.none;
20458         }
20459     }
20460
20461     // define once
20462     var stripRe = /[\$,%]/g;
20463
20464     // prebuilt conversion function for this field, instead of
20465     // switching every time we're reading a value
20466     if(!this.convert){
20467         var cv, dateFormat = this.dateFormat;
20468         switch(this.type){
20469             case "":
20470             case "auto":
20471             case undefined:
20472                 cv = function(v){ return v; };
20473                 break;
20474             case "string":
20475                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20476                 break;
20477             case "int":
20478                 cv = function(v){
20479                     return v !== undefined && v !== null && v !== '' ?
20480                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20481                     };
20482                 break;
20483             case "float":
20484                 cv = function(v){
20485                     return v !== undefined && v !== null && v !== '' ?
20486                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20487                     };
20488                 break;
20489             case "bool":
20490             case "boolean":
20491                 cv = function(v){ return v === true || v === "true" || v == 1; };
20492                 break;
20493             case "date":
20494                 cv = function(v){
20495                     if(!v){
20496                         return '';
20497                     }
20498                     if(v instanceof Date){
20499                         return v;
20500                     }
20501                     if(dateFormat){
20502                         if(dateFormat == "timestamp"){
20503                             return new Date(v*1000);
20504                         }
20505                         return Date.parseDate(v, dateFormat);
20506                     }
20507                     var parsed = Date.parse(v);
20508                     return parsed ? new Date(parsed) : null;
20509                 };
20510              break;
20511             
20512         }
20513         this.convert = cv;
20514     }
20515 };
20516
20517 Roo.data.Field.prototype = {
20518     dateFormat: null,
20519     defaultValue: "",
20520     mapping: null,
20521     sortType : null,
20522     sortDir : "ASC"
20523 };/*
20524  * Based on:
20525  * Ext JS Library 1.1.1
20526  * Copyright(c) 2006-2007, Ext JS, LLC.
20527  *
20528  * Originally Released Under LGPL - original licence link has changed is not relivant.
20529  *
20530  * Fork - LGPL
20531  * <script type="text/javascript">
20532  */
20533  
20534 // Base class for reading structured data from a data source.  This class is intended to be
20535 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20536
20537 /**
20538  * @class Roo.data.DataReader
20539  * Base class for reading structured data from a data source.  This class is intended to be
20540  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20541  */
20542
20543 Roo.data.DataReader = function(meta, recordType){
20544     
20545     this.meta = meta;
20546     
20547     this.recordType = recordType instanceof Array ? 
20548         Roo.data.Record.create(recordType) : recordType;
20549 };
20550
20551 Roo.data.DataReader.prototype = {
20552      /**
20553      * Create an empty record
20554      * @param {Object} data (optional) - overlay some values
20555      * @return {Roo.data.Record} record created.
20556      */
20557     newRow :  function(d) {
20558         var da =  {};
20559         this.recordType.prototype.fields.each(function(c) {
20560             switch( c.type) {
20561                 case 'int' : da[c.name] = 0; break;
20562                 case 'date' : da[c.name] = new Date(); break;
20563                 case 'float' : da[c.name] = 0.0; break;
20564                 case 'boolean' : da[c.name] = false; break;
20565                 default : da[c.name] = ""; break;
20566             }
20567             
20568         });
20569         return new this.recordType(Roo.apply(da, d));
20570     }
20571     
20572 };/*
20573  * Based on:
20574  * Ext JS Library 1.1.1
20575  * Copyright(c) 2006-2007, Ext JS, LLC.
20576  *
20577  * Originally Released Under LGPL - original licence link has changed is not relivant.
20578  *
20579  * Fork - LGPL
20580  * <script type="text/javascript">
20581  */
20582
20583 /**
20584  * @class Roo.data.DataProxy
20585  * @extends Roo.data.Observable
20586  * This class is an abstract base class for implementations which provide retrieval of
20587  * unformatted data objects.<br>
20588  * <p>
20589  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20590  * (of the appropriate type which knows how to parse the data object) to provide a block of
20591  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20592  * <p>
20593  * Custom implementations must implement the load method as described in
20594  * {@link Roo.data.HttpProxy#load}.
20595  */
20596 Roo.data.DataProxy = function(){
20597     this.addEvents({
20598         /**
20599          * @event beforeload
20600          * Fires before a network request is made to retrieve a data object.
20601          * @param {Object} This DataProxy object.
20602          * @param {Object} params The params parameter to the load function.
20603          */
20604         beforeload : true,
20605         /**
20606          * @event load
20607          * Fires before the load method's callback is called.
20608          * @param {Object} This DataProxy object.
20609          * @param {Object} o The data object.
20610          * @param {Object} arg The callback argument object passed to the load function.
20611          */
20612         load : true,
20613         /**
20614          * @event loadexception
20615          * Fires if an Exception occurs during data retrieval.
20616          * @param {Object} This DataProxy object.
20617          * @param {Object} o The data object.
20618          * @param {Object} arg The callback argument object passed to the load function.
20619          * @param {Object} e The Exception.
20620          */
20621         loadexception : true
20622     });
20623     Roo.data.DataProxy.superclass.constructor.call(this);
20624 };
20625
20626 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20627
20628     /**
20629      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20630      */
20631 /*
20632  * Based on:
20633  * Ext JS Library 1.1.1
20634  * Copyright(c) 2006-2007, Ext JS, LLC.
20635  *
20636  * Originally Released Under LGPL - original licence link has changed is not relivant.
20637  *
20638  * Fork - LGPL
20639  * <script type="text/javascript">
20640  */
20641 /**
20642  * @class Roo.data.MemoryProxy
20643  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20644  * to the Reader when its load method is called.
20645  * @constructor
20646  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20647  */
20648 Roo.data.MemoryProxy = function(data){
20649     if (data.data) {
20650         data = data.data;
20651     }
20652     Roo.data.MemoryProxy.superclass.constructor.call(this);
20653     this.data = data;
20654 };
20655
20656 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20657     /**
20658      * Load data from the requested source (in this case an in-memory
20659      * data object passed to the constructor), read the data object into
20660      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20661      * process that block using the passed callback.
20662      * @param {Object} params This parameter is not used by the MemoryProxy class.
20663      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20664      * object into a block of Roo.data.Records.
20665      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20666      * The function must be passed <ul>
20667      * <li>The Record block object</li>
20668      * <li>The "arg" argument from the load function</li>
20669      * <li>A boolean success indicator</li>
20670      * </ul>
20671      * @param {Object} scope The scope in which to call the callback
20672      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20673      */
20674     load : function(params, reader, callback, scope, arg){
20675         params = params || {};
20676         var result;
20677         try {
20678             result = reader.readRecords(this.data);
20679         }catch(e){
20680             this.fireEvent("loadexception", this, arg, null, e);
20681             callback.call(scope, null, arg, false);
20682             return;
20683         }
20684         callback.call(scope, result, arg, true);
20685     },
20686     
20687     // private
20688     update : function(params, records){
20689         
20690     }
20691 });/*
20692  * Based on:
20693  * Ext JS Library 1.1.1
20694  * Copyright(c) 2006-2007, Ext JS, LLC.
20695  *
20696  * Originally Released Under LGPL - original licence link has changed is not relivant.
20697  *
20698  * Fork - LGPL
20699  * <script type="text/javascript">
20700  */
20701 /**
20702  * @class Roo.data.HttpProxy
20703  * @extends Roo.data.DataProxy
20704  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20705  * configured to reference a certain URL.<br><br>
20706  * <p>
20707  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20708  * from which the running page was served.<br><br>
20709  * <p>
20710  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20711  * <p>
20712  * Be aware that to enable the browser to parse an XML document, the server must set
20713  * the Content-Type header in the HTTP response to "text/xml".
20714  * @constructor
20715  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20716  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20717  * will be used to make the request.
20718  */
20719 Roo.data.HttpProxy = function(conn){
20720     Roo.data.HttpProxy.superclass.constructor.call(this);
20721     // is conn a conn config or a real conn?
20722     this.conn = conn;
20723     this.useAjax = !conn || !conn.events;
20724   
20725 };
20726
20727 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20728     // thse are take from connection...
20729     
20730     /**
20731      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20732      */
20733     /**
20734      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20735      * extra parameters to each request made by this object. (defaults to undefined)
20736      */
20737     /**
20738      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20739      *  to each request made by this object. (defaults to undefined)
20740      */
20741     /**
20742      * @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)
20743      */
20744     /**
20745      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20746      */
20747      /**
20748      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20749      * @type Boolean
20750      */
20751   
20752
20753     /**
20754      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20755      * @type Boolean
20756      */
20757     /**
20758      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20759      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20760      * a finer-grained basis than the DataProxy events.
20761      */
20762     getConnection : function(){
20763         return this.useAjax ? Roo.Ajax : this.conn;
20764     },
20765
20766     /**
20767      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20768      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20769      * process that block using the passed callback.
20770      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20771      * for the request to the remote server.
20772      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20773      * object into a block of Roo.data.Records.
20774      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20775      * The function must be passed <ul>
20776      * <li>The Record block object</li>
20777      * <li>The "arg" argument from the load function</li>
20778      * <li>A boolean success indicator</li>
20779      * </ul>
20780      * @param {Object} scope The scope in which to call the callback
20781      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20782      */
20783     load : function(params, reader, callback, scope, arg){
20784         if(this.fireEvent("beforeload", this, params) !== false){
20785             var  o = {
20786                 params : params || {},
20787                 request: {
20788                     callback : callback,
20789                     scope : scope,
20790                     arg : arg
20791                 },
20792                 reader: reader,
20793                 callback : this.loadResponse,
20794                 scope: this
20795             };
20796             if(this.useAjax){
20797                 Roo.applyIf(o, this.conn);
20798                 if(this.activeRequest){
20799                     Roo.Ajax.abort(this.activeRequest);
20800                 }
20801                 this.activeRequest = Roo.Ajax.request(o);
20802             }else{
20803                 this.conn.request(o);
20804             }
20805         }else{
20806             callback.call(scope||this, null, arg, false);
20807         }
20808     },
20809
20810     // private
20811     loadResponse : function(o, success, response){
20812         delete this.activeRequest;
20813         if(!success){
20814             this.fireEvent("loadexception", this, o, response);
20815             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20816             return;
20817         }
20818         var result;
20819         try {
20820             result = o.reader.read(response);
20821         }catch(e){
20822             this.fireEvent("loadexception", this, o, response, e);
20823             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20824             return;
20825         }
20826         
20827         this.fireEvent("load", this, o, o.request.arg);
20828         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20829     },
20830
20831     // private
20832     update : function(dataSet){
20833
20834     },
20835
20836     // private
20837     updateResponse : function(dataSet){
20838
20839     }
20840 });/*
20841  * Based on:
20842  * Ext JS Library 1.1.1
20843  * Copyright(c) 2006-2007, Ext JS, LLC.
20844  *
20845  * Originally Released Under LGPL - original licence link has changed is not relivant.
20846  *
20847  * Fork - LGPL
20848  * <script type="text/javascript">
20849  */
20850
20851 /**
20852  * @class Roo.data.ScriptTagProxy
20853  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20854  * other than the originating domain of the running page.<br><br>
20855  * <p>
20856  * <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
20857  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20858  * <p>
20859  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20860  * source code that is used as the source inside a &lt;script> tag.<br><br>
20861  * <p>
20862  * In order for the browser to process the returned data, the server must wrap the data object
20863  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20864  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20865  * depending on whether the callback name was passed:
20866  * <p>
20867  * <pre><code>
20868 boolean scriptTag = false;
20869 String cb = request.getParameter("callback");
20870 if (cb != null) {
20871     scriptTag = true;
20872     response.setContentType("text/javascript");
20873 } else {
20874     response.setContentType("application/x-json");
20875 }
20876 Writer out = response.getWriter();
20877 if (scriptTag) {
20878     out.write(cb + "(");
20879 }
20880 out.print(dataBlock.toJsonString());
20881 if (scriptTag) {
20882     out.write(");");
20883 }
20884 </pre></code>
20885  *
20886  * @constructor
20887  * @param {Object} config A configuration object.
20888  */
20889 Roo.data.ScriptTagProxy = function(config){
20890     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20891     Roo.apply(this, config);
20892     this.head = document.getElementsByTagName("head")[0];
20893 };
20894
20895 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20896
20897 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20898     /**
20899      * @cfg {String} url The URL from which to request the data object.
20900      */
20901     /**
20902      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20903      */
20904     timeout : 30000,
20905     /**
20906      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20907      * the server the name of the callback function set up by the load call to process the returned data object.
20908      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20909      * javascript output which calls this named function passing the data object as its only parameter.
20910      */
20911     callbackParam : "callback",
20912     /**
20913      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20914      * name to the request.
20915      */
20916     nocache : true,
20917
20918     /**
20919      * Load data from the configured URL, read the data object into
20920      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20921      * process that block using the passed callback.
20922      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20923      * for the request to the remote server.
20924      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20925      * object into a block of Roo.data.Records.
20926      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20927      * The function must be passed <ul>
20928      * <li>The Record block object</li>
20929      * <li>The "arg" argument from the load function</li>
20930      * <li>A boolean success indicator</li>
20931      * </ul>
20932      * @param {Object} scope The scope in which to call the callback
20933      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20934      */
20935     load : function(params, reader, callback, scope, arg){
20936         if(this.fireEvent("beforeload", this, params) !== false){
20937
20938             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20939
20940             var url = this.url;
20941             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20942             if(this.nocache){
20943                 url += "&_dc=" + (new Date().getTime());
20944             }
20945             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20946             var trans = {
20947                 id : transId,
20948                 cb : "stcCallback"+transId,
20949                 scriptId : "stcScript"+transId,
20950                 params : params,
20951                 arg : arg,
20952                 url : url,
20953                 callback : callback,
20954                 scope : scope,
20955                 reader : reader
20956             };
20957             var conn = this;
20958
20959             window[trans.cb] = function(o){
20960                 conn.handleResponse(o, trans);
20961             };
20962
20963             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20964
20965             if(this.autoAbort !== false){
20966                 this.abort();
20967             }
20968
20969             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20970
20971             var script = document.createElement("script");
20972             script.setAttribute("src", url);
20973             script.setAttribute("type", "text/javascript");
20974             script.setAttribute("id", trans.scriptId);
20975             this.head.appendChild(script);
20976
20977             this.trans = trans;
20978         }else{
20979             callback.call(scope||this, null, arg, false);
20980         }
20981     },
20982
20983     // private
20984     isLoading : function(){
20985         return this.trans ? true : false;
20986     },
20987
20988     /**
20989      * Abort the current server request.
20990      */
20991     abort : function(){
20992         if(this.isLoading()){
20993             this.destroyTrans(this.trans);
20994         }
20995     },
20996
20997     // private
20998     destroyTrans : function(trans, isLoaded){
20999         this.head.removeChild(document.getElementById(trans.scriptId));
21000         clearTimeout(trans.timeoutId);
21001         if(isLoaded){
21002             window[trans.cb] = undefined;
21003             try{
21004                 delete window[trans.cb];
21005             }catch(e){}
21006         }else{
21007             // if hasn't been loaded, wait for load to remove it to prevent script error
21008             window[trans.cb] = function(){
21009                 window[trans.cb] = undefined;
21010                 try{
21011                     delete window[trans.cb];
21012                 }catch(e){}
21013             };
21014         }
21015     },
21016
21017     // private
21018     handleResponse : function(o, trans){
21019         this.trans = false;
21020         this.destroyTrans(trans, true);
21021         var result;
21022         try {
21023             result = trans.reader.readRecords(o);
21024         }catch(e){
21025             this.fireEvent("loadexception", this, o, trans.arg, e);
21026             trans.callback.call(trans.scope||window, null, trans.arg, false);
21027             return;
21028         }
21029         this.fireEvent("load", this, o, trans.arg);
21030         trans.callback.call(trans.scope||window, result, trans.arg, true);
21031     },
21032
21033     // private
21034     handleFailure : function(trans){
21035         this.trans = false;
21036         this.destroyTrans(trans, false);
21037         this.fireEvent("loadexception", this, null, trans.arg);
21038         trans.callback.call(trans.scope||window, null, trans.arg, false);
21039     }
21040 });/*
21041  * Based on:
21042  * Ext JS Library 1.1.1
21043  * Copyright(c) 2006-2007, Ext JS, LLC.
21044  *
21045  * Originally Released Under LGPL - original licence link has changed is not relivant.
21046  *
21047  * Fork - LGPL
21048  * <script type="text/javascript">
21049  */
21050
21051 /**
21052  * @class Roo.data.JsonReader
21053  * @extends Roo.data.DataReader
21054  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
21055  * based on mappings in a provided Roo.data.Record constructor.
21056  * 
21057  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
21058  * in the reply previously. 
21059  * 
21060  * <p>
21061  * Example code:
21062  * <pre><code>
21063 var RecordDef = Roo.data.Record.create([
21064     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21065     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21066 ]);
21067 var myReader = new Roo.data.JsonReader({
21068     totalProperty: "results",    // The property which contains the total dataset size (optional)
21069     root: "rows",                // The property which contains an Array of row objects
21070     id: "id"                     // The property within each row object that provides an ID for the record (optional)
21071 }, RecordDef);
21072 </code></pre>
21073  * <p>
21074  * This would consume a JSON file like this:
21075  * <pre><code>
21076 { 'results': 2, 'rows': [
21077     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
21078     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
21079 }
21080 </code></pre>
21081  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
21082  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21083  * paged from the remote server.
21084  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
21085  * @cfg {String} root name of the property which contains the Array of row objects.
21086  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
21087  * @constructor
21088  * Create a new JsonReader
21089  * @param {Object} meta Metadata configuration options
21090  * @param {Object} recordType Either an Array of field definition objects,
21091  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
21092  */
21093 Roo.data.JsonReader = function(meta, recordType){
21094     
21095     meta = meta || {};
21096     // set some defaults:
21097     Roo.applyIf(meta, {
21098         totalProperty: 'total',
21099         successProperty : 'success',
21100         root : 'data',
21101         id : 'id'
21102     });
21103     
21104     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21105 };
21106 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
21107     
21108     /**
21109      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
21110      * Used by Store query builder to append _requestMeta to params.
21111      * 
21112      */
21113     metaFromRemote : false,
21114     /**
21115      * This method is only used by a DataProxy which has retrieved data from a remote server.
21116      * @param {Object} response The XHR object which contains the JSON data in its responseText.
21117      * @return {Object} data A data block which is used by an Roo.data.Store object as
21118      * a cache of Roo.data.Records.
21119      */
21120     read : function(response){
21121         var json = response.responseText;
21122        
21123         var o = /* eval:var:o */ eval("("+json+")");
21124         if(!o) {
21125             throw {message: "JsonReader.read: Json object not found"};
21126         }
21127         
21128         if(o.metaData){
21129             
21130             delete this.ef;
21131             this.metaFromRemote = true;
21132             this.meta = o.metaData;
21133             this.recordType = Roo.data.Record.create(o.metaData.fields);
21134             this.onMetaChange(this.meta, this.recordType, o);
21135         }
21136         return this.readRecords(o);
21137     },
21138
21139     // private function a store will implement
21140     onMetaChange : function(meta, recordType, o){
21141
21142     },
21143
21144     /**
21145          * @ignore
21146          */
21147     simpleAccess: function(obj, subsc) {
21148         return obj[subsc];
21149     },
21150
21151         /**
21152          * @ignore
21153          */
21154     getJsonAccessor: function(){
21155         var re = /[\[\.]/;
21156         return function(expr) {
21157             try {
21158                 return(re.test(expr))
21159                     ? new Function("obj", "return obj." + expr)
21160                     : function(obj){
21161                         return obj[expr];
21162                     };
21163             } catch(e){}
21164             return Roo.emptyFn;
21165         };
21166     }(),
21167
21168     /**
21169      * Create a data block containing Roo.data.Records from an XML document.
21170      * @param {Object} o An object which contains an Array of row objects in the property specified
21171      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
21172      * which contains the total size of the dataset.
21173      * @return {Object} data A data block which is used by an Roo.data.Store object as
21174      * a cache of Roo.data.Records.
21175      */
21176     readRecords : function(o){
21177         /**
21178          * After any data loads, the raw JSON data is available for further custom processing.
21179          * @type Object
21180          */
21181         this.o = o;
21182         var s = this.meta, Record = this.recordType,
21183             f = Record.prototype.fields, fi = f.items, fl = f.length;
21184
21185 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
21186         if (!this.ef) {
21187             if(s.totalProperty) {
21188                     this.getTotal = this.getJsonAccessor(s.totalProperty);
21189                 }
21190                 if(s.successProperty) {
21191                     this.getSuccess = this.getJsonAccessor(s.successProperty);
21192                 }
21193                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
21194                 if (s.id) {
21195                         var g = this.getJsonAccessor(s.id);
21196                         this.getId = function(rec) {
21197                                 var r = g(rec);
21198                                 return (r === undefined || r === "") ? null : r;
21199                         };
21200                 } else {
21201                         this.getId = function(){return null;};
21202                 }
21203             this.ef = [];
21204             for(var jj = 0; jj < fl; jj++){
21205                 f = fi[jj];
21206                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
21207                 this.ef[jj] = this.getJsonAccessor(map);
21208             }
21209         }
21210
21211         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
21212         if(s.totalProperty){
21213             var vt = parseInt(this.getTotal(o), 10);
21214             if(!isNaN(vt)){
21215                 totalRecords = vt;
21216             }
21217         }
21218         if(s.successProperty){
21219             var vs = this.getSuccess(o);
21220             if(vs === false || vs === 'false'){
21221                 success = false;
21222             }
21223         }
21224         var records = [];
21225             for(var i = 0; i < c; i++){
21226                     var n = root[i];
21227                 var values = {};
21228                 var id = this.getId(n);
21229                 for(var j = 0; j < fl; j++){
21230                     f = fi[j];
21231                 var v = this.ef[j](n);
21232                 if (!f.convert) {
21233                     Roo.log('missing convert for ' + f.name);
21234                     Roo.log(f);
21235                     continue;
21236                 }
21237                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
21238                 }
21239                 var record = new Record(values, id);
21240                 record.json = n;
21241                 records[i] = record;
21242             }
21243             return {
21244             raw : o,
21245                 success : success,
21246                 records : records,
21247                 totalRecords : totalRecords
21248             };
21249     }
21250 });/*
21251  * Based on:
21252  * Ext JS Library 1.1.1
21253  * Copyright(c) 2006-2007, Ext JS, LLC.
21254  *
21255  * Originally Released Under LGPL - original licence link has changed is not relivant.
21256  *
21257  * Fork - LGPL
21258  * <script type="text/javascript">
21259  */
21260
21261 /**
21262  * @class Roo.data.XmlReader
21263  * @extends Roo.data.DataReader
21264  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
21265  * based on mappings in a provided Roo.data.Record constructor.<br><br>
21266  * <p>
21267  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
21268  * header in the HTTP response must be set to "text/xml".</em>
21269  * <p>
21270  * Example code:
21271  * <pre><code>
21272 var RecordDef = Roo.data.Record.create([
21273    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21274    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21275 ]);
21276 var myReader = new Roo.data.XmlReader({
21277    totalRecords: "results", // The element which contains the total dataset size (optional)
21278    record: "row",           // The repeated element which contains row information
21279    id: "id"                 // The element within the row that provides an ID for the record (optional)
21280 }, RecordDef);
21281 </code></pre>
21282  * <p>
21283  * This would consume an XML file like this:
21284  * <pre><code>
21285 &lt;?xml?>
21286 &lt;dataset>
21287  &lt;results>2&lt;/results>
21288  &lt;row>
21289    &lt;id>1&lt;/id>
21290    &lt;name>Bill&lt;/name>
21291    &lt;occupation>Gardener&lt;/occupation>
21292  &lt;/row>
21293  &lt;row>
21294    &lt;id>2&lt;/id>
21295    &lt;name>Ben&lt;/name>
21296    &lt;occupation>Horticulturalist&lt;/occupation>
21297  &lt;/row>
21298 &lt;/dataset>
21299 </code></pre>
21300  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
21301  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21302  * paged from the remote server.
21303  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
21304  * @cfg {String} success The DomQuery path to the success attribute used by forms.
21305  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
21306  * a record identifier value.
21307  * @constructor
21308  * Create a new XmlReader
21309  * @param {Object} meta Metadata configuration options
21310  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
21311  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
21312  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
21313  */
21314 Roo.data.XmlReader = function(meta, recordType){
21315     meta = meta || {};
21316     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21317 };
21318 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
21319     /**
21320      * This method is only used by a DataProxy which has retrieved data from a remote server.
21321          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
21322          * to contain a method called 'responseXML' that returns an XML document object.
21323      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21324      * a cache of Roo.data.Records.
21325      */
21326     read : function(response){
21327         var doc = response.responseXML;
21328         if(!doc) {
21329             throw {message: "XmlReader.read: XML Document not available"};
21330         }
21331         return this.readRecords(doc);
21332     },
21333
21334     /**
21335      * Create a data block containing Roo.data.Records from an XML document.
21336          * @param {Object} doc A parsed XML document.
21337      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21338      * a cache of Roo.data.Records.
21339      */
21340     readRecords : function(doc){
21341         /**
21342          * After any data loads/reads, the raw XML Document is available for further custom processing.
21343          * @type XMLDocument
21344          */
21345         this.xmlData = doc;
21346         var root = doc.documentElement || doc;
21347         var q = Roo.DomQuery;
21348         var recordType = this.recordType, fields = recordType.prototype.fields;
21349         var sid = this.meta.id;
21350         var totalRecords = 0, success = true;
21351         if(this.meta.totalRecords){
21352             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
21353         }
21354         
21355         if(this.meta.success){
21356             var sv = q.selectValue(this.meta.success, root, true);
21357             success = sv !== false && sv !== 'false';
21358         }
21359         var records = [];
21360         var ns = q.select(this.meta.record, root);
21361         for(var i = 0, len = ns.length; i < len; i++) {
21362                 var n = ns[i];
21363                 var values = {};
21364                 var id = sid ? q.selectValue(sid, n) : undefined;
21365                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21366                     var f = fields.items[j];
21367                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
21368                     v = f.convert(v);
21369                     values[f.name] = v;
21370                 }
21371                 var record = new recordType(values, id);
21372                 record.node = n;
21373                 records[records.length] = record;
21374             }
21375
21376             return {
21377                 success : success,
21378                 records : records,
21379                 totalRecords : totalRecords || records.length
21380             };
21381     }
21382 });/*
21383  * Based on:
21384  * Ext JS Library 1.1.1
21385  * Copyright(c) 2006-2007, Ext JS, LLC.
21386  *
21387  * Originally Released Under LGPL - original licence link has changed is not relivant.
21388  *
21389  * Fork - LGPL
21390  * <script type="text/javascript">
21391  */
21392
21393 /**
21394  * @class Roo.data.ArrayReader
21395  * @extends Roo.data.DataReader
21396  * Data reader class to create an Array of Roo.data.Record objects from an Array.
21397  * Each element of that Array represents a row of data fields. The
21398  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
21399  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
21400  * <p>
21401  * Example code:.
21402  * <pre><code>
21403 var RecordDef = Roo.data.Record.create([
21404     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
21405     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
21406 ]);
21407 var myReader = new Roo.data.ArrayReader({
21408     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
21409 }, RecordDef);
21410 </code></pre>
21411  * <p>
21412  * This would consume an Array like this:
21413  * <pre><code>
21414 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
21415   </code></pre>
21416  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
21417  * @constructor
21418  * Create a new JsonReader
21419  * @param {Object} meta Metadata configuration options.
21420  * @param {Object} recordType Either an Array of field definition objects
21421  * as specified to {@link Roo.data.Record#create},
21422  * or an {@link Roo.data.Record} object
21423  * created using {@link Roo.data.Record#create}.
21424  */
21425 Roo.data.ArrayReader = function(meta, recordType){
21426     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
21427 };
21428
21429 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
21430     /**
21431      * Create a data block containing Roo.data.Records from an XML document.
21432      * @param {Object} o An Array of row objects which represents the dataset.
21433      * @return {Object} data A data block which is used by an Roo.data.Store object as
21434      * a cache of Roo.data.Records.
21435      */
21436     readRecords : function(o){
21437         var sid = this.meta ? this.meta.id : null;
21438         var recordType = this.recordType, fields = recordType.prototype.fields;
21439         var records = [];
21440         var root = o;
21441             for(var i = 0; i < root.length; i++){
21442                     var n = root[i];
21443                 var values = {};
21444                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
21445                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21446                 var f = fields.items[j];
21447                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
21448                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
21449                 v = f.convert(v);
21450                 values[f.name] = v;
21451             }
21452                 var record = new recordType(values, id);
21453                 record.json = n;
21454                 records[records.length] = record;
21455             }
21456             return {
21457                 records : records,
21458                 totalRecords : records.length
21459             };
21460     }
21461 });/*
21462  * Based on:
21463  * Ext JS Library 1.1.1
21464  * Copyright(c) 2006-2007, Ext JS, LLC.
21465  *
21466  * Originally Released Under LGPL - original licence link has changed is not relivant.
21467  *
21468  * Fork - LGPL
21469  * <script type="text/javascript">
21470  */
21471
21472
21473 /**
21474  * @class Roo.data.Tree
21475  * @extends Roo.util.Observable
21476  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21477  * in the tree have most standard DOM functionality.
21478  * @constructor
21479  * @param {Node} root (optional) The root node
21480  */
21481 Roo.data.Tree = function(root){
21482    this.nodeHash = {};
21483    /**
21484     * The root node for this tree
21485     * @type Node
21486     */
21487    this.root = null;
21488    if(root){
21489        this.setRootNode(root);
21490    }
21491    this.addEvents({
21492        /**
21493         * @event append
21494         * Fires when a new child node is appended to a node in this tree.
21495         * @param {Tree} tree The owner tree
21496         * @param {Node} parent The parent node
21497         * @param {Node} node The newly appended node
21498         * @param {Number} index The index of the newly appended node
21499         */
21500        "append" : true,
21501        /**
21502         * @event remove
21503         * Fires when a child node is removed from a node in this tree.
21504         * @param {Tree} tree The owner tree
21505         * @param {Node} parent The parent node
21506         * @param {Node} node The child node removed
21507         */
21508        "remove" : true,
21509        /**
21510         * @event move
21511         * Fires when a node is moved to a new location in the tree
21512         * @param {Tree} tree The owner tree
21513         * @param {Node} node The node moved
21514         * @param {Node} oldParent The old parent of this node
21515         * @param {Node} newParent The new parent of this node
21516         * @param {Number} index The index it was moved to
21517         */
21518        "move" : true,
21519        /**
21520         * @event insert
21521         * Fires when a new child node is inserted in a node in this tree.
21522         * @param {Tree} tree The owner tree
21523         * @param {Node} parent The parent node
21524         * @param {Node} node The child node inserted
21525         * @param {Node} refNode The child node the node was inserted before
21526         */
21527        "insert" : true,
21528        /**
21529         * @event beforeappend
21530         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21531         * @param {Tree} tree The owner tree
21532         * @param {Node} parent The parent node
21533         * @param {Node} node The child node to be appended
21534         */
21535        "beforeappend" : true,
21536        /**
21537         * @event beforeremove
21538         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21539         * @param {Tree} tree The owner tree
21540         * @param {Node} parent The parent node
21541         * @param {Node} node The child node to be removed
21542         */
21543        "beforeremove" : true,
21544        /**
21545         * @event beforemove
21546         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21547         * @param {Tree} tree The owner tree
21548         * @param {Node} node The node being moved
21549         * @param {Node} oldParent The parent of the node
21550         * @param {Node} newParent The new parent the node is moving to
21551         * @param {Number} index The index it is being moved to
21552         */
21553        "beforemove" : true,
21554        /**
21555         * @event beforeinsert
21556         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21557         * @param {Tree} tree The owner tree
21558         * @param {Node} parent The parent node
21559         * @param {Node} node The child node to be inserted
21560         * @param {Node} refNode The child node the node is being inserted before
21561         */
21562        "beforeinsert" : true
21563    });
21564
21565     Roo.data.Tree.superclass.constructor.call(this);
21566 };
21567
21568 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21569     pathSeparator: "/",
21570
21571     proxyNodeEvent : function(){
21572         return this.fireEvent.apply(this, arguments);
21573     },
21574
21575     /**
21576      * Returns the root node for this tree.
21577      * @return {Node}
21578      */
21579     getRootNode : function(){
21580         return this.root;
21581     },
21582
21583     /**
21584      * Sets the root node for this tree.
21585      * @param {Node} node
21586      * @return {Node}
21587      */
21588     setRootNode : function(node){
21589         this.root = node;
21590         node.ownerTree = this;
21591         node.isRoot = true;
21592         this.registerNode(node);
21593         return node;
21594     },
21595
21596     /**
21597      * Gets a node in this tree by its id.
21598      * @param {String} id
21599      * @return {Node}
21600      */
21601     getNodeById : function(id){
21602         return this.nodeHash[id];
21603     },
21604
21605     registerNode : function(node){
21606         this.nodeHash[node.id] = node;
21607     },
21608
21609     unregisterNode : function(node){
21610         delete this.nodeHash[node.id];
21611     },
21612
21613     toString : function(){
21614         return "[Tree"+(this.id?" "+this.id:"")+"]";
21615     }
21616 });
21617
21618 /**
21619  * @class Roo.data.Node
21620  * @extends Roo.util.Observable
21621  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21622  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21623  * @constructor
21624  * @param {Object} attributes The attributes/config for the node
21625  */
21626 Roo.data.Node = function(attributes){
21627     /**
21628      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21629      * @type {Object}
21630      */
21631     this.attributes = attributes || {};
21632     this.leaf = this.attributes.leaf;
21633     /**
21634      * The node id. @type String
21635      */
21636     this.id = this.attributes.id;
21637     if(!this.id){
21638         this.id = Roo.id(null, "ynode-");
21639         this.attributes.id = this.id;
21640     }
21641      
21642     
21643     /**
21644      * All child nodes of this node. @type Array
21645      */
21646     this.childNodes = [];
21647     if(!this.childNodes.indexOf){ // indexOf is a must
21648         this.childNodes.indexOf = function(o){
21649             for(var i = 0, len = this.length; i < len; i++){
21650                 if(this[i] == o) {
21651                     return i;
21652                 }
21653             }
21654             return -1;
21655         };
21656     }
21657     /**
21658      * The parent node for this node. @type Node
21659      */
21660     this.parentNode = null;
21661     /**
21662      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21663      */
21664     this.firstChild = null;
21665     /**
21666      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21667      */
21668     this.lastChild = null;
21669     /**
21670      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21671      */
21672     this.previousSibling = null;
21673     /**
21674      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21675      */
21676     this.nextSibling = null;
21677
21678     this.addEvents({
21679        /**
21680         * @event append
21681         * Fires when a new child node is appended
21682         * @param {Tree} tree The owner tree
21683         * @param {Node} this This node
21684         * @param {Node} node The newly appended node
21685         * @param {Number} index The index of the newly appended node
21686         */
21687        "append" : true,
21688        /**
21689         * @event remove
21690         * Fires when a child node is removed
21691         * @param {Tree} tree The owner tree
21692         * @param {Node} this This node
21693         * @param {Node} node The removed node
21694         */
21695        "remove" : true,
21696        /**
21697         * @event move
21698         * Fires when this node is moved to a new location in the tree
21699         * @param {Tree} tree The owner tree
21700         * @param {Node} this This node
21701         * @param {Node} oldParent The old parent of this node
21702         * @param {Node} newParent The new parent of this node
21703         * @param {Number} index The index it was moved to
21704         */
21705        "move" : true,
21706        /**
21707         * @event insert
21708         * Fires when a new child node is inserted.
21709         * @param {Tree} tree The owner tree
21710         * @param {Node} this This node
21711         * @param {Node} node The child node inserted
21712         * @param {Node} refNode The child node the node was inserted before
21713         */
21714        "insert" : true,
21715        /**
21716         * @event beforeappend
21717         * Fires before a new child is appended, return false to cancel the append.
21718         * @param {Tree} tree The owner tree
21719         * @param {Node} this This node
21720         * @param {Node} node The child node to be appended
21721         */
21722        "beforeappend" : true,
21723        /**
21724         * @event beforeremove
21725         * Fires before a child is removed, return false to cancel the remove.
21726         * @param {Tree} tree The owner tree
21727         * @param {Node} this This node
21728         * @param {Node} node The child node to be removed
21729         */
21730        "beforeremove" : true,
21731        /**
21732         * @event beforemove
21733         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21734         * @param {Tree} tree The owner tree
21735         * @param {Node} this This node
21736         * @param {Node} oldParent The parent of this node
21737         * @param {Node} newParent The new parent this node is moving to
21738         * @param {Number} index The index it is being moved to
21739         */
21740        "beforemove" : true,
21741        /**
21742         * @event beforeinsert
21743         * Fires before a new child is inserted, return false to cancel the insert.
21744         * @param {Tree} tree The owner tree
21745         * @param {Node} this This node
21746         * @param {Node} node The child node to be inserted
21747         * @param {Node} refNode The child node the node is being inserted before
21748         */
21749        "beforeinsert" : true
21750    });
21751     this.listeners = this.attributes.listeners;
21752     Roo.data.Node.superclass.constructor.call(this);
21753 };
21754
21755 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21756     fireEvent : function(evtName){
21757         // first do standard event for this node
21758         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21759             return false;
21760         }
21761         // then bubble it up to the tree if the event wasn't cancelled
21762         var ot = this.getOwnerTree();
21763         if(ot){
21764             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21765                 return false;
21766             }
21767         }
21768         return true;
21769     },
21770
21771     /**
21772      * Returns true if this node is a leaf
21773      * @return {Boolean}
21774      */
21775     isLeaf : function(){
21776         return this.leaf === true;
21777     },
21778
21779     // private
21780     setFirstChild : function(node){
21781         this.firstChild = node;
21782     },
21783
21784     //private
21785     setLastChild : function(node){
21786         this.lastChild = node;
21787     },
21788
21789
21790     /**
21791      * Returns true if this node is the last child of its parent
21792      * @return {Boolean}
21793      */
21794     isLast : function(){
21795        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21796     },
21797
21798     /**
21799      * Returns true if this node is the first child of its parent
21800      * @return {Boolean}
21801      */
21802     isFirst : function(){
21803        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21804     },
21805
21806     hasChildNodes : function(){
21807         return !this.isLeaf() && this.childNodes.length > 0;
21808     },
21809
21810     /**
21811      * Insert node(s) as the last child node of this node.
21812      * @param {Node/Array} node The node or Array of nodes to append
21813      * @return {Node} The appended node if single append, or null if an array was passed
21814      */
21815     appendChild : function(node){
21816         var multi = false;
21817         if(node instanceof Array){
21818             multi = node;
21819         }else if(arguments.length > 1){
21820             multi = arguments;
21821         }
21822         // if passed an array or multiple args do them one by one
21823         if(multi){
21824             for(var i = 0, len = multi.length; i < len; i++) {
21825                 this.appendChild(multi[i]);
21826             }
21827         }else{
21828             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21829                 return false;
21830             }
21831             var index = this.childNodes.length;
21832             var oldParent = node.parentNode;
21833             // it's a move, make sure we move it cleanly
21834             if(oldParent){
21835                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21836                     return false;
21837                 }
21838                 oldParent.removeChild(node);
21839             }
21840             index = this.childNodes.length;
21841             if(index == 0){
21842                 this.setFirstChild(node);
21843             }
21844             this.childNodes.push(node);
21845             node.parentNode = this;
21846             var ps = this.childNodes[index-1];
21847             if(ps){
21848                 node.previousSibling = ps;
21849                 ps.nextSibling = node;
21850             }else{
21851                 node.previousSibling = null;
21852             }
21853             node.nextSibling = null;
21854             this.setLastChild(node);
21855             node.setOwnerTree(this.getOwnerTree());
21856             this.fireEvent("append", this.ownerTree, this, node, index);
21857             if(oldParent){
21858                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21859             }
21860             return node;
21861         }
21862     },
21863
21864     /**
21865      * Removes a child node from this node.
21866      * @param {Node} node The node to remove
21867      * @return {Node} The removed node
21868      */
21869     removeChild : function(node){
21870         var index = this.childNodes.indexOf(node);
21871         if(index == -1){
21872             return false;
21873         }
21874         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21875             return false;
21876         }
21877
21878         // remove it from childNodes collection
21879         this.childNodes.splice(index, 1);
21880
21881         // update siblings
21882         if(node.previousSibling){
21883             node.previousSibling.nextSibling = node.nextSibling;
21884         }
21885         if(node.nextSibling){
21886             node.nextSibling.previousSibling = node.previousSibling;
21887         }
21888
21889         // update child refs
21890         if(this.firstChild == node){
21891             this.setFirstChild(node.nextSibling);
21892         }
21893         if(this.lastChild == node){
21894             this.setLastChild(node.previousSibling);
21895         }
21896
21897         node.setOwnerTree(null);
21898         // clear any references from the node
21899         node.parentNode = null;
21900         node.previousSibling = null;
21901         node.nextSibling = null;
21902         this.fireEvent("remove", this.ownerTree, this, node);
21903         return node;
21904     },
21905
21906     /**
21907      * Inserts the first node before the second node in this nodes childNodes collection.
21908      * @param {Node} node The node to insert
21909      * @param {Node} refNode The node to insert before (if null the node is appended)
21910      * @return {Node} The inserted node
21911      */
21912     insertBefore : function(node, refNode){
21913         if(!refNode){ // like standard Dom, refNode can be null for append
21914             return this.appendChild(node);
21915         }
21916         // nothing to do
21917         if(node == refNode){
21918             return false;
21919         }
21920
21921         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21922             return false;
21923         }
21924         var index = this.childNodes.indexOf(refNode);
21925         var oldParent = node.parentNode;
21926         var refIndex = index;
21927
21928         // when moving internally, indexes will change after remove
21929         if(oldParent == this && this.childNodes.indexOf(node) < index){
21930             refIndex--;
21931         }
21932
21933         // it's a move, make sure we move it cleanly
21934         if(oldParent){
21935             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21936                 return false;
21937             }
21938             oldParent.removeChild(node);
21939         }
21940         if(refIndex == 0){
21941             this.setFirstChild(node);
21942         }
21943         this.childNodes.splice(refIndex, 0, node);
21944         node.parentNode = this;
21945         var ps = this.childNodes[refIndex-1];
21946         if(ps){
21947             node.previousSibling = ps;
21948             ps.nextSibling = node;
21949         }else{
21950             node.previousSibling = null;
21951         }
21952         node.nextSibling = refNode;
21953         refNode.previousSibling = node;
21954         node.setOwnerTree(this.getOwnerTree());
21955         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21956         if(oldParent){
21957             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21958         }
21959         return node;
21960     },
21961
21962     /**
21963      * Returns the child node at the specified index.
21964      * @param {Number} index
21965      * @return {Node}
21966      */
21967     item : function(index){
21968         return this.childNodes[index];
21969     },
21970
21971     /**
21972      * Replaces one child node in this node with another.
21973      * @param {Node} newChild The replacement node
21974      * @param {Node} oldChild The node to replace
21975      * @return {Node} The replaced node
21976      */
21977     replaceChild : function(newChild, oldChild){
21978         this.insertBefore(newChild, oldChild);
21979         this.removeChild(oldChild);
21980         return oldChild;
21981     },
21982
21983     /**
21984      * Returns the index of a child node
21985      * @param {Node} node
21986      * @return {Number} The index of the node or -1 if it was not found
21987      */
21988     indexOf : function(child){
21989         return this.childNodes.indexOf(child);
21990     },
21991
21992     /**
21993      * Returns the tree this node is in.
21994      * @return {Tree}
21995      */
21996     getOwnerTree : function(){
21997         // if it doesn't have one, look for one
21998         if(!this.ownerTree){
21999             var p = this;
22000             while(p){
22001                 if(p.ownerTree){
22002                     this.ownerTree = p.ownerTree;
22003                     break;
22004                 }
22005                 p = p.parentNode;
22006             }
22007         }
22008         return this.ownerTree;
22009     },
22010
22011     /**
22012      * Returns depth of this node (the root node has a depth of 0)
22013      * @return {Number}
22014      */
22015     getDepth : function(){
22016         var depth = 0;
22017         var p = this;
22018         while(p.parentNode){
22019             ++depth;
22020             p = p.parentNode;
22021         }
22022         return depth;
22023     },
22024
22025     // private
22026     setOwnerTree : function(tree){
22027         // if it's move, we need to update everyone
22028         if(tree != this.ownerTree){
22029             if(this.ownerTree){
22030                 this.ownerTree.unregisterNode(this);
22031             }
22032             this.ownerTree = tree;
22033             var cs = this.childNodes;
22034             for(var i = 0, len = cs.length; i < len; i++) {
22035                 cs[i].setOwnerTree(tree);
22036             }
22037             if(tree){
22038                 tree.registerNode(this);
22039             }
22040         }
22041     },
22042
22043     /**
22044      * Returns the path for this node. The path can be used to expand or select this node programmatically.
22045      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
22046      * @return {String} The path
22047      */
22048     getPath : function(attr){
22049         attr = attr || "id";
22050         var p = this.parentNode;
22051         var b = [this.attributes[attr]];
22052         while(p){
22053             b.unshift(p.attributes[attr]);
22054             p = p.parentNode;
22055         }
22056         var sep = this.getOwnerTree().pathSeparator;
22057         return sep + b.join(sep);
22058     },
22059
22060     /**
22061      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22062      * function call will be the scope provided or the current node. The arguments to the function
22063      * will be the args provided or the current node. If the function returns false at any point,
22064      * the bubble is stopped.
22065      * @param {Function} fn The function to call
22066      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22067      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22068      */
22069     bubble : function(fn, scope, args){
22070         var p = this;
22071         while(p){
22072             if(fn.call(scope || p, args || p) === false){
22073                 break;
22074             }
22075             p = p.parentNode;
22076         }
22077     },
22078
22079     /**
22080      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22081      * function call will be the scope provided or the current node. The arguments to the function
22082      * will be the args provided or the current node. If the function returns false at any point,
22083      * the cascade is stopped on that branch.
22084      * @param {Function} fn The function to call
22085      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22086      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22087      */
22088     cascade : function(fn, scope, args){
22089         if(fn.call(scope || this, args || this) !== false){
22090             var cs = this.childNodes;
22091             for(var i = 0, len = cs.length; i < len; i++) {
22092                 cs[i].cascade(fn, scope, args);
22093             }
22094         }
22095     },
22096
22097     /**
22098      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
22099      * function call will be the scope provided or the current node. The arguments to the function
22100      * will be the args provided or the current node. If the function returns false at any point,
22101      * the iteration stops.
22102      * @param {Function} fn The function to call
22103      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22104      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22105      */
22106     eachChild : function(fn, scope, args){
22107         var cs = this.childNodes;
22108         for(var i = 0, len = cs.length; i < len; i++) {
22109                 if(fn.call(scope || this, args || cs[i]) === false){
22110                     break;
22111                 }
22112         }
22113     },
22114
22115     /**
22116      * Finds the first child that has the attribute with the specified value.
22117      * @param {String} attribute The attribute name
22118      * @param {Mixed} value The value to search for
22119      * @return {Node} The found child or null if none was found
22120      */
22121     findChild : function(attribute, value){
22122         var cs = this.childNodes;
22123         for(var i = 0, len = cs.length; i < len; i++) {
22124                 if(cs[i].attributes[attribute] == value){
22125                     return cs[i];
22126                 }
22127         }
22128         return null;
22129     },
22130
22131     /**
22132      * Finds the first child by a custom function. The child matches if the function passed
22133      * returns true.
22134      * @param {Function} fn
22135      * @param {Object} scope (optional)
22136      * @return {Node} The found child or null if none was found
22137      */
22138     findChildBy : function(fn, scope){
22139         var cs = this.childNodes;
22140         for(var i = 0, len = cs.length; i < len; i++) {
22141                 if(fn.call(scope||cs[i], cs[i]) === true){
22142                     return cs[i];
22143                 }
22144         }
22145         return null;
22146     },
22147
22148     /**
22149      * Sorts this nodes children using the supplied sort function
22150      * @param {Function} fn
22151      * @param {Object} scope (optional)
22152      */
22153     sort : function(fn, scope){
22154         var cs = this.childNodes;
22155         var len = cs.length;
22156         if(len > 0){
22157             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
22158             cs.sort(sortFn);
22159             for(var i = 0; i < len; i++){
22160                 var n = cs[i];
22161                 n.previousSibling = cs[i-1];
22162                 n.nextSibling = cs[i+1];
22163                 if(i == 0){
22164                     this.setFirstChild(n);
22165                 }
22166                 if(i == len-1){
22167                     this.setLastChild(n);
22168                 }
22169             }
22170         }
22171     },
22172
22173     /**
22174      * Returns true if this node is an ancestor (at any point) of the passed node.
22175      * @param {Node} node
22176      * @return {Boolean}
22177      */
22178     contains : function(node){
22179         return node.isAncestor(this);
22180     },
22181
22182     /**
22183      * Returns true if the passed node is an ancestor (at any point) of this node.
22184      * @param {Node} node
22185      * @return {Boolean}
22186      */
22187     isAncestor : function(node){
22188         var p = this.parentNode;
22189         while(p){
22190             if(p == node){
22191                 return true;
22192             }
22193             p = p.parentNode;
22194         }
22195         return false;
22196     },
22197
22198     toString : function(){
22199         return "[Node"+(this.id?" "+this.id:"")+"]";
22200     }
22201 });/*
22202  * Based on:
22203  * Ext JS Library 1.1.1
22204  * Copyright(c) 2006-2007, Ext JS, LLC.
22205  *
22206  * Originally Released Under LGPL - original licence link has changed is not relivant.
22207  *
22208  * Fork - LGPL
22209  * <script type="text/javascript">
22210  */
22211  
22212
22213 /**
22214  * @class Roo.ComponentMgr
22215  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
22216  * @singleton
22217  */
22218 Roo.ComponentMgr = function(){
22219     var all = new Roo.util.MixedCollection();
22220
22221     return {
22222         /**
22223          * Registers a component.
22224          * @param {Roo.Component} c The component
22225          */
22226         register : function(c){
22227             all.add(c);
22228         },
22229
22230         /**
22231          * Unregisters a component.
22232          * @param {Roo.Component} c The component
22233          */
22234         unregister : function(c){
22235             all.remove(c);
22236         },
22237
22238         /**
22239          * Returns a component by id
22240          * @param {String} id The component id
22241          */
22242         get : function(id){
22243             return all.get(id);
22244         },
22245
22246         /**
22247          * Registers a function that will be called when a specified component is added to ComponentMgr
22248          * @param {String} id The component id
22249          * @param {Funtction} fn The callback function
22250          * @param {Object} scope The scope of the callback
22251          */
22252         onAvailable : function(id, fn, scope){
22253             all.on("add", function(index, o){
22254                 if(o.id == id){
22255                     fn.call(scope || o, o);
22256                     all.un("add", fn, scope);
22257                 }
22258             });
22259         }
22260     };
22261 }();/*
22262  * Based on:
22263  * Ext JS Library 1.1.1
22264  * Copyright(c) 2006-2007, Ext JS, LLC.
22265  *
22266  * Originally Released Under LGPL - original licence link has changed is not relivant.
22267  *
22268  * Fork - LGPL
22269  * <script type="text/javascript">
22270  */
22271  
22272 /**
22273  * @class Roo.Component
22274  * @extends Roo.util.Observable
22275  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
22276  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
22277  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
22278  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
22279  * All visual components (widgets) that require rendering into a layout should subclass Component.
22280  * @constructor
22281  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
22282  * 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
22283  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
22284  */
22285 Roo.Component = function(config){
22286     config = config || {};
22287     if(config.tagName || config.dom || typeof config == "string"){ // element object
22288         config = {el: config, id: config.id || config};
22289     }
22290     this.initialConfig = config;
22291
22292     Roo.apply(this, config);
22293     this.addEvents({
22294         /**
22295          * @event disable
22296          * Fires after the component is disabled.
22297              * @param {Roo.Component} this
22298              */
22299         disable : true,
22300         /**
22301          * @event enable
22302          * Fires after the component is enabled.
22303              * @param {Roo.Component} this
22304              */
22305         enable : true,
22306         /**
22307          * @event beforeshow
22308          * Fires before the component is shown.  Return false to stop the show.
22309              * @param {Roo.Component} this
22310              */
22311         beforeshow : true,
22312         /**
22313          * @event show
22314          * Fires after the component is shown.
22315              * @param {Roo.Component} this
22316              */
22317         show : true,
22318         /**
22319          * @event beforehide
22320          * Fires before the component is hidden. Return false to stop the hide.
22321              * @param {Roo.Component} this
22322              */
22323         beforehide : true,
22324         /**
22325          * @event hide
22326          * Fires after the component is hidden.
22327              * @param {Roo.Component} this
22328              */
22329         hide : true,
22330         /**
22331          * @event beforerender
22332          * Fires before the component is rendered. Return false to stop the render.
22333              * @param {Roo.Component} this
22334              */
22335         beforerender : true,
22336         /**
22337          * @event render
22338          * Fires after the component is rendered.
22339              * @param {Roo.Component} this
22340              */
22341         render : true,
22342         /**
22343          * @event beforedestroy
22344          * Fires before the component is destroyed. Return false to stop the destroy.
22345              * @param {Roo.Component} this
22346              */
22347         beforedestroy : true,
22348         /**
22349          * @event destroy
22350          * Fires after the component is destroyed.
22351              * @param {Roo.Component} this
22352              */
22353         destroy : true
22354     });
22355     if(!this.id){
22356         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
22357     }
22358     Roo.ComponentMgr.register(this);
22359     Roo.Component.superclass.constructor.call(this);
22360     this.initComponent();
22361     if(this.renderTo){ // not supported by all components yet. use at your own risk!
22362         this.render(this.renderTo);
22363         delete this.renderTo;
22364     }
22365 };
22366
22367 /** @private */
22368 Roo.Component.AUTO_ID = 1000;
22369
22370 Roo.extend(Roo.Component, Roo.util.Observable, {
22371     /**
22372      * @scope Roo.Component.prototype
22373      * @type {Boolean}
22374      * true if this component is hidden. Read-only.
22375      */
22376     hidden : false,
22377     /**
22378      * @type {Boolean}
22379      * true if this component is disabled. Read-only.
22380      */
22381     disabled : false,
22382     /**
22383      * @type {Boolean}
22384      * true if this component has been rendered. Read-only.
22385      */
22386     rendered : false,
22387     
22388     /** @cfg {String} disableClass
22389      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
22390      */
22391     disabledClass : "x-item-disabled",
22392         /** @cfg {Boolean} allowDomMove
22393          * Whether the component can move the Dom node when rendering (defaults to true).
22394          */
22395     allowDomMove : true,
22396     /** @cfg {String} hideMode
22397      * How this component should hidden. Supported values are
22398      * "visibility" (css visibility), "offsets" (negative offset position) and
22399      * "display" (css display) - defaults to "display".
22400      */
22401     hideMode: 'display',
22402
22403     /** @private */
22404     ctype : "Roo.Component",
22405
22406     /**
22407      * @cfg {String} actionMode 
22408      * which property holds the element that used for  hide() / show() / disable() / enable()
22409      * default is 'el' 
22410      */
22411     actionMode : "el",
22412
22413     /** @private */
22414     getActionEl : function(){
22415         return this[this.actionMode];
22416     },
22417
22418     initComponent : Roo.emptyFn,
22419     /**
22420      * If this is a lazy rendering component, render it to its container element.
22421      * @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.
22422      */
22423     render : function(container, position){
22424         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
22425             if(!container && this.el){
22426                 this.el = Roo.get(this.el);
22427                 container = this.el.dom.parentNode;
22428                 this.allowDomMove = false;
22429             }
22430             this.container = Roo.get(container);
22431             this.rendered = true;
22432             if(position !== undefined){
22433                 if(typeof position == 'number'){
22434                     position = this.container.dom.childNodes[position];
22435                 }else{
22436                     position = Roo.getDom(position);
22437                 }
22438             }
22439             this.onRender(this.container, position || null);
22440             if(this.cls){
22441                 this.el.addClass(this.cls);
22442                 delete this.cls;
22443             }
22444             if(this.style){
22445                 this.el.applyStyles(this.style);
22446                 delete this.style;
22447             }
22448             this.fireEvent("render", this);
22449             this.afterRender(this.container);
22450             if(this.hidden){
22451                 this.hide();
22452             }
22453             if(this.disabled){
22454                 this.disable();
22455             }
22456         }
22457         return this;
22458     },
22459
22460     /** @private */
22461     // default function is not really useful
22462     onRender : function(ct, position){
22463         if(this.el){
22464             this.el = Roo.get(this.el);
22465             if(this.allowDomMove !== false){
22466                 ct.dom.insertBefore(this.el.dom, position);
22467             }
22468         }
22469     },
22470
22471     /** @private */
22472     getAutoCreate : function(){
22473         var cfg = typeof this.autoCreate == "object" ?
22474                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
22475         if(this.id && !cfg.id){
22476             cfg.id = this.id;
22477         }
22478         return cfg;
22479     },
22480
22481     /** @private */
22482     afterRender : Roo.emptyFn,
22483
22484     /**
22485      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22486      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22487      */
22488     destroy : function(){
22489         if(this.fireEvent("beforedestroy", this) !== false){
22490             this.purgeListeners();
22491             this.beforeDestroy();
22492             if(this.rendered){
22493                 this.el.removeAllListeners();
22494                 this.el.remove();
22495                 if(this.actionMode == "container"){
22496                     this.container.remove();
22497                 }
22498             }
22499             this.onDestroy();
22500             Roo.ComponentMgr.unregister(this);
22501             this.fireEvent("destroy", this);
22502         }
22503     },
22504
22505         /** @private */
22506     beforeDestroy : function(){
22507
22508     },
22509
22510         /** @private */
22511         onDestroy : function(){
22512
22513     },
22514
22515     /**
22516      * Returns the underlying {@link Roo.Element}.
22517      * @return {Roo.Element} The element
22518      */
22519     getEl : function(){
22520         return this.el;
22521     },
22522
22523     /**
22524      * Returns the id of this component.
22525      * @return {String}
22526      */
22527     getId : function(){
22528         return this.id;
22529     },
22530
22531     /**
22532      * Try to focus this component.
22533      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22534      * @return {Roo.Component} this
22535      */
22536     focus : function(selectText){
22537         if(this.rendered){
22538             this.el.focus();
22539             if(selectText === true){
22540                 this.el.dom.select();
22541             }
22542         }
22543         return this;
22544     },
22545
22546     /** @private */
22547     blur : function(){
22548         if(this.rendered){
22549             this.el.blur();
22550         }
22551         return this;
22552     },
22553
22554     /**
22555      * Disable this component.
22556      * @return {Roo.Component} this
22557      */
22558     disable : function(){
22559         if(this.rendered){
22560             this.onDisable();
22561         }
22562         this.disabled = true;
22563         this.fireEvent("disable", this);
22564         return this;
22565     },
22566
22567         // private
22568     onDisable : function(){
22569         this.getActionEl().addClass(this.disabledClass);
22570         this.el.dom.disabled = true;
22571     },
22572
22573     /**
22574      * Enable this component.
22575      * @return {Roo.Component} this
22576      */
22577     enable : function(){
22578         if(this.rendered){
22579             this.onEnable();
22580         }
22581         this.disabled = false;
22582         this.fireEvent("enable", this);
22583         return this;
22584     },
22585
22586         // private
22587     onEnable : function(){
22588         this.getActionEl().removeClass(this.disabledClass);
22589         this.el.dom.disabled = false;
22590     },
22591
22592     /**
22593      * Convenience function for setting disabled/enabled by boolean.
22594      * @param {Boolean} disabled
22595      */
22596     setDisabled : function(disabled){
22597         this[disabled ? "disable" : "enable"]();
22598     },
22599
22600     /**
22601      * Show this component.
22602      * @return {Roo.Component} this
22603      */
22604     show: function(){
22605         if(this.fireEvent("beforeshow", this) !== false){
22606             this.hidden = false;
22607             if(this.rendered){
22608                 this.onShow();
22609             }
22610             this.fireEvent("show", this);
22611         }
22612         return this;
22613     },
22614
22615     // private
22616     onShow : function(){
22617         var ae = this.getActionEl();
22618         if(this.hideMode == 'visibility'){
22619             ae.dom.style.visibility = "visible";
22620         }else if(this.hideMode == 'offsets'){
22621             ae.removeClass('x-hidden');
22622         }else{
22623             ae.dom.style.display = "";
22624         }
22625     },
22626
22627     /**
22628      * Hide this component.
22629      * @return {Roo.Component} this
22630      */
22631     hide: function(){
22632         if(this.fireEvent("beforehide", this) !== false){
22633             this.hidden = true;
22634             if(this.rendered){
22635                 this.onHide();
22636             }
22637             this.fireEvent("hide", this);
22638         }
22639         return this;
22640     },
22641
22642     // private
22643     onHide : function(){
22644         var ae = this.getActionEl();
22645         if(this.hideMode == 'visibility'){
22646             ae.dom.style.visibility = "hidden";
22647         }else if(this.hideMode == 'offsets'){
22648             ae.addClass('x-hidden');
22649         }else{
22650             ae.dom.style.display = "none";
22651         }
22652     },
22653
22654     /**
22655      * Convenience function to hide or show this component by boolean.
22656      * @param {Boolean} visible True to show, false to hide
22657      * @return {Roo.Component} this
22658      */
22659     setVisible: function(visible){
22660         if(visible) {
22661             this.show();
22662         }else{
22663             this.hide();
22664         }
22665         return this;
22666     },
22667
22668     /**
22669      * Returns true if this component is visible.
22670      */
22671     isVisible : function(){
22672         return this.getActionEl().isVisible();
22673     },
22674
22675     cloneConfig : function(overrides){
22676         overrides = overrides || {};
22677         var id = overrides.id || Roo.id();
22678         var cfg = Roo.applyIf(overrides, this.initialConfig);
22679         cfg.id = id; // prevent dup id
22680         return new this.constructor(cfg);
22681     }
22682 });/*
22683  * Based on:
22684  * Ext JS Library 1.1.1
22685  * Copyright(c) 2006-2007, Ext JS, LLC.
22686  *
22687  * Originally Released Under LGPL - original licence link has changed is not relivant.
22688  *
22689  * Fork - LGPL
22690  * <script type="text/javascript">
22691  */
22692  (function(){ 
22693 /**
22694  * @class Roo.Layer
22695  * @extends Roo.Element
22696  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22697  * automatic maintaining of shadow/shim positions.
22698  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22699  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22700  * you can pass a string with a CSS class name. False turns off the shadow.
22701  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22702  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22703  * @cfg {String} cls CSS class to add to the element
22704  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22705  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22706  * @constructor
22707  * @param {Object} config An object with config options.
22708  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22709  */
22710
22711 Roo.Layer = function(config, existingEl){
22712     config = config || {};
22713     var dh = Roo.DomHelper;
22714     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22715     if(existingEl){
22716         this.dom = Roo.getDom(existingEl);
22717     }
22718     if(!this.dom){
22719         var o = config.dh || {tag: "div", cls: "x-layer"};
22720         this.dom = dh.append(pel, o);
22721     }
22722     if(config.cls){
22723         this.addClass(config.cls);
22724     }
22725     this.constrain = config.constrain !== false;
22726     this.visibilityMode = Roo.Element.VISIBILITY;
22727     if(config.id){
22728         this.id = this.dom.id = config.id;
22729     }else{
22730         this.id = Roo.id(this.dom);
22731     }
22732     this.zindex = config.zindex || this.getZIndex();
22733     this.position("absolute", this.zindex);
22734     if(config.shadow){
22735         this.shadowOffset = config.shadowOffset || 4;
22736         this.shadow = new Roo.Shadow({
22737             offset : this.shadowOffset,
22738             mode : config.shadow
22739         });
22740     }else{
22741         this.shadowOffset = 0;
22742     }
22743     this.useShim = config.shim !== false && Roo.useShims;
22744     this.useDisplay = config.useDisplay;
22745     this.hide();
22746 };
22747
22748 var supr = Roo.Element.prototype;
22749
22750 // shims are shared among layer to keep from having 100 iframes
22751 var shims = [];
22752
22753 Roo.extend(Roo.Layer, Roo.Element, {
22754
22755     getZIndex : function(){
22756         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22757     },
22758
22759     getShim : function(){
22760         if(!this.useShim){
22761             return null;
22762         }
22763         if(this.shim){
22764             return this.shim;
22765         }
22766         var shim = shims.shift();
22767         if(!shim){
22768             shim = this.createShim();
22769             shim.enableDisplayMode('block');
22770             shim.dom.style.display = 'none';
22771             shim.dom.style.visibility = 'visible';
22772         }
22773         var pn = this.dom.parentNode;
22774         if(shim.dom.parentNode != pn){
22775             pn.insertBefore(shim.dom, this.dom);
22776         }
22777         shim.setStyle('z-index', this.getZIndex()-2);
22778         this.shim = shim;
22779         return shim;
22780     },
22781
22782     hideShim : function(){
22783         if(this.shim){
22784             this.shim.setDisplayed(false);
22785             shims.push(this.shim);
22786             delete this.shim;
22787         }
22788     },
22789
22790     disableShadow : function(){
22791         if(this.shadow){
22792             this.shadowDisabled = true;
22793             this.shadow.hide();
22794             this.lastShadowOffset = this.shadowOffset;
22795             this.shadowOffset = 0;
22796         }
22797     },
22798
22799     enableShadow : function(show){
22800         if(this.shadow){
22801             this.shadowDisabled = false;
22802             this.shadowOffset = this.lastShadowOffset;
22803             delete this.lastShadowOffset;
22804             if(show){
22805                 this.sync(true);
22806             }
22807         }
22808     },
22809
22810     // private
22811     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22812     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22813     sync : function(doShow){
22814         var sw = this.shadow;
22815         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22816             var sh = this.getShim();
22817
22818             var w = this.getWidth(),
22819                 h = this.getHeight();
22820
22821             var l = this.getLeft(true),
22822                 t = this.getTop(true);
22823
22824             if(sw && !this.shadowDisabled){
22825                 if(doShow && !sw.isVisible()){
22826                     sw.show(this);
22827                 }else{
22828                     sw.realign(l, t, w, h);
22829                 }
22830                 if(sh){
22831                     if(doShow){
22832                        sh.show();
22833                     }
22834                     // fit the shim behind the shadow, so it is shimmed too
22835                     var a = sw.adjusts, s = sh.dom.style;
22836                     s.left = (Math.min(l, l+a.l))+"px";
22837                     s.top = (Math.min(t, t+a.t))+"px";
22838                     s.width = (w+a.w)+"px";
22839                     s.height = (h+a.h)+"px";
22840                 }
22841             }else if(sh){
22842                 if(doShow){
22843                    sh.show();
22844                 }
22845                 sh.setSize(w, h);
22846                 sh.setLeftTop(l, t);
22847             }
22848             
22849         }
22850     },
22851
22852     // private
22853     destroy : function(){
22854         this.hideShim();
22855         if(this.shadow){
22856             this.shadow.hide();
22857         }
22858         this.removeAllListeners();
22859         var pn = this.dom.parentNode;
22860         if(pn){
22861             pn.removeChild(this.dom);
22862         }
22863         Roo.Element.uncache(this.id);
22864     },
22865
22866     remove : function(){
22867         this.destroy();
22868     },
22869
22870     // private
22871     beginUpdate : function(){
22872         this.updating = true;
22873     },
22874
22875     // private
22876     endUpdate : function(){
22877         this.updating = false;
22878         this.sync(true);
22879     },
22880
22881     // private
22882     hideUnders : function(negOffset){
22883         if(this.shadow){
22884             this.shadow.hide();
22885         }
22886         this.hideShim();
22887     },
22888
22889     // private
22890     constrainXY : function(){
22891         if(this.constrain){
22892             var vw = Roo.lib.Dom.getViewWidth(),
22893                 vh = Roo.lib.Dom.getViewHeight();
22894             var s = Roo.get(document).getScroll();
22895
22896             var xy = this.getXY();
22897             var x = xy[0], y = xy[1];   
22898             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22899             // only move it if it needs it
22900             var moved = false;
22901             // first validate right/bottom
22902             if((x + w) > vw+s.left){
22903                 x = vw - w - this.shadowOffset;
22904                 moved = true;
22905             }
22906             if((y + h) > vh+s.top){
22907                 y = vh - h - this.shadowOffset;
22908                 moved = true;
22909             }
22910             // then make sure top/left isn't negative
22911             if(x < s.left){
22912                 x = s.left;
22913                 moved = true;
22914             }
22915             if(y < s.top){
22916                 y = s.top;
22917                 moved = true;
22918             }
22919             if(moved){
22920                 if(this.avoidY){
22921                     var ay = this.avoidY;
22922                     if(y <= ay && (y+h) >= ay){
22923                         y = ay-h-5;   
22924                     }
22925                 }
22926                 xy = [x, y];
22927                 this.storeXY(xy);
22928                 supr.setXY.call(this, xy);
22929                 this.sync();
22930             }
22931         }
22932     },
22933
22934     isVisible : function(){
22935         return this.visible;    
22936     },
22937
22938     // private
22939     showAction : function(){
22940         this.visible = true; // track visibility to prevent getStyle calls
22941         if(this.useDisplay === true){
22942             this.setDisplayed("");
22943         }else if(this.lastXY){
22944             supr.setXY.call(this, this.lastXY);
22945         }else if(this.lastLT){
22946             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22947         }
22948     },
22949
22950     // private
22951     hideAction : function(){
22952         this.visible = false;
22953         if(this.useDisplay === true){
22954             this.setDisplayed(false);
22955         }else{
22956             this.setLeftTop(-10000,-10000);
22957         }
22958     },
22959
22960     // overridden Element method
22961     setVisible : function(v, a, d, c, e){
22962         if(v){
22963             this.showAction();
22964         }
22965         if(a && v){
22966             var cb = function(){
22967                 this.sync(true);
22968                 if(c){
22969                     c();
22970                 }
22971             }.createDelegate(this);
22972             supr.setVisible.call(this, true, true, d, cb, e);
22973         }else{
22974             if(!v){
22975                 this.hideUnders(true);
22976             }
22977             var cb = c;
22978             if(a){
22979                 cb = function(){
22980                     this.hideAction();
22981                     if(c){
22982                         c();
22983                     }
22984                 }.createDelegate(this);
22985             }
22986             supr.setVisible.call(this, v, a, d, cb, e);
22987             if(v){
22988                 this.sync(true);
22989             }else if(!a){
22990                 this.hideAction();
22991             }
22992         }
22993     },
22994
22995     storeXY : function(xy){
22996         delete this.lastLT;
22997         this.lastXY = xy;
22998     },
22999
23000     storeLeftTop : function(left, top){
23001         delete this.lastXY;
23002         this.lastLT = [left, top];
23003     },
23004
23005     // private
23006     beforeFx : function(){
23007         this.beforeAction();
23008         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23009     },
23010
23011     // private
23012     afterFx : function(){
23013         Roo.Layer.superclass.afterFx.apply(this, arguments);
23014         this.sync(this.isVisible());
23015     },
23016
23017     // private
23018     beforeAction : function(){
23019         if(!this.updating && this.shadow){
23020             this.shadow.hide();
23021         }
23022     },
23023
23024     // overridden Element method
23025     setLeft : function(left){
23026         this.storeLeftTop(left, this.getTop(true));
23027         supr.setLeft.apply(this, arguments);
23028         this.sync();
23029     },
23030
23031     setTop : function(top){
23032         this.storeLeftTop(this.getLeft(true), top);
23033         supr.setTop.apply(this, arguments);
23034         this.sync();
23035     },
23036
23037     setLeftTop : function(left, top){
23038         this.storeLeftTop(left, top);
23039         supr.setLeftTop.apply(this, arguments);
23040         this.sync();
23041     },
23042
23043     setXY : function(xy, a, d, c, e){
23044         this.fixDisplay();
23045         this.beforeAction();
23046         this.storeXY(xy);
23047         var cb = this.createCB(c);
23048         supr.setXY.call(this, xy, a, d, cb, e);
23049         if(!a){
23050             cb();
23051         }
23052     },
23053
23054     // private
23055     createCB : function(c){
23056         var el = this;
23057         return function(){
23058             el.constrainXY();
23059             el.sync(true);
23060             if(c){
23061                 c();
23062             }
23063         };
23064     },
23065
23066     // overridden Element method
23067     setX : function(x, a, d, c, e){
23068         this.setXY([x, this.getY()], a, d, c, e);
23069     },
23070
23071     // overridden Element method
23072     setY : function(y, a, d, c, e){
23073         this.setXY([this.getX(), y], a, d, c, e);
23074     },
23075
23076     // overridden Element method
23077     setSize : function(w, h, a, d, c, e){
23078         this.beforeAction();
23079         var cb = this.createCB(c);
23080         supr.setSize.call(this, w, h, a, d, cb, e);
23081         if(!a){
23082             cb();
23083         }
23084     },
23085
23086     // overridden Element method
23087     setWidth : function(w, a, d, c, e){
23088         this.beforeAction();
23089         var cb = this.createCB(c);
23090         supr.setWidth.call(this, w, a, d, cb, e);
23091         if(!a){
23092             cb();
23093         }
23094     },
23095
23096     // overridden Element method
23097     setHeight : function(h, a, d, c, e){
23098         this.beforeAction();
23099         var cb = this.createCB(c);
23100         supr.setHeight.call(this, h, a, d, cb, e);
23101         if(!a){
23102             cb();
23103         }
23104     },
23105
23106     // overridden Element method
23107     setBounds : function(x, y, w, h, a, d, c, e){
23108         this.beforeAction();
23109         var cb = this.createCB(c);
23110         if(!a){
23111             this.storeXY([x, y]);
23112             supr.setXY.call(this, [x, y]);
23113             supr.setSize.call(this, w, h, a, d, cb, e);
23114             cb();
23115         }else{
23116             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
23117         }
23118         return this;
23119     },
23120     
23121     /**
23122      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
23123      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
23124      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
23125      * @param {Number} zindex The new z-index to set
23126      * @return {this} The Layer
23127      */
23128     setZIndex : function(zindex){
23129         this.zindex = zindex;
23130         this.setStyle("z-index", zindex + 2);
23131         if(this.shadow){
23132             this.shadow.setZIndex(zindex + 1);
23133         }
23134         if(this.shim){
23135             this.shim.setStyle("z-index", zindex);
23136         }
23137     }
23138 });
23139 })();/*
23140  * Based on:
23141  * Ext JS Library 1.1.1
23142  * Copyright(c) 2006-2007, Ext JS, LLC.
23143  *
23144  * Originally Released Under LGPL - original licence link has changed is not relivant.
23145  *
23146  * Fork - LGPL
23147  * <script type="text/javascript">
23148  */
23149
23150
23151 /**
23152  * @class Roo.Shadow
23153  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
23154  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
23155  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
23156  * @constructor
23157  * Create a new Shadow
23158  * @param {Object} config The config object
23159  */
23160 Roo.Shadow = function(config){
23161     Roo.apply(this, config);
23162     if(typeof this.mode != "string"){
23163         this.mode = this.defaultMode;
23164     }
23165     var o = this.offset, a = {h: 0};
23166     var rad = Math.floor(this.offset/2);
23167     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
23168         case "drop":
23169             a.w = 0;
23170             a.l = a.t = o;
23171             a.t -= 1;
23172             if(Roo.isIE){
23173                 a.l -= this.offset + rad;
23174                 a.t -= this.offset + rad;
23175                 a.w -= rad;
23176                 a.h -= rad;
23177                 a.t += 1;
23178             }
23179         break;
23180         case "sides":
23181             a.w = (o*2);
23182             a.l = -o;
23183             a.t = o-1;
23184             if(Roo.isIE){
23185                 a.l -= (this.offset - rad);
23186                 a.t -= this.offset + rad;
23187                 a.l += 1;
23188                 a.w -= (this.offset - rad)*2;
23189                 a.w -= rad + 1;
23190                 a.h -= 1;
23191             }
23192         break;
23193         case "frame":
23194             a.w = a.h = (o*2);
23195             a.l = a.t = -o;
23196             a.t += 1;
23197             a.h -= 2;
23198             if(Roo.isIE){
23199                 a.l -= (this.offset - rad);
23200                 a.t -= (this.offset - rad);
23201                 a.l += 1;
23202                 a.w -= (this.offset + rad + 1);
23203                 a.h -= (this.offset + rad);
23204                 a.h += 1;
23205             }
23206         break;
23207     };
23208
23209     this.adjusts = a;
23210 };
23211
23212 Roo.Shadow.prototype = {
23213     /**
23214      * @cfg {String} mode
23215      * The shadow display mode.  Supports the following options:<br />
23216      * sides: Shadow displays on both sides and bottom only<br />
23217      * frame: Shadow displays equally on all four sides<br />
23218      * drop: Traditional bottom-right drop shadow (default)
23219      */
23220     /**
23221      * @cfg {String} offset
23222      * The number of pixels to offset the shadow from the element (defaults to 4)
23223      */
23224     offset: 4,
23225
23226     // private
23227     defaultMode: "drop",
23228
23229     /**
23230      * Displays the shadow under the target element
23231      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
23232      */
23233     show : function(target){
23234         target = Roo.get(target);
23235         if(!this.el){
23236             this.el = Roo.Shadow.Pool.pull();
23237             if(this.el.dom.nextSibling != target.dom){
23238                 this.el.insertBefore(target);
23239             }
23240         }
23241         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
23242         if(Roo.isIE){
23243             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
23244         }
23245         this.realign(
23246             target.getLeft(true),
23247             target.getTop(true),
23248             target.getWidth(),
23249             target.getHeight()
23250         );
23251         this.el.dom.style.display = "block";
23252     },
23253
23254     /**
23255      * Returns true if the shadow is visible, else false
23256      */
23257     isVisible : function(){
23258         return this.el ? true : false;  
23259     },
23260
23261     /**
23262      * Direct alignment when values are already available. Show must be called at least once before
23263      * calling this method to ensure it is initialized.
23264      * @param {Number} left The target element left position
23265      * @param {Number} top The target element top position
23266      * @param {Number} width The target element width
23267      * @param {Number} height The target element height
23268      */
23269     realign : function(l, t, w, h){
23270         if(!this.el){
23271             return;
23272         }
23273         var a = this.adjusts, d = this.el.dom, s = d.style;
23274         var iea = 0;
23275         s.left = (l+a.l)+"px";
23276         s.top = (t+a.t)+"px";
23277         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
23278  
23279         if(s.width != sws || s.height != shs){
23280             s.width = sws;
23281             s.height = shs;
23282             if(!Roo.isIE){
23283                 var cn = d.childNodes;
23284                 var sww = Math.max(0, (sw-12))+"px";
23285                 cn[0].childNodes[1].style.width = sww;
23286                 cn[1].childNodes[1].style.width = sww;
23287                 cn[2].childNodes[1].style.width = sww;
23288                 cn[1].style.height = Math.max(0, (sh-12))+"px";
23289             }
23290         }
23291     },
23292
23293     /**
23294      * Hides this shadow
23295      */
23296     hide : function(){
23297         if(this.el){
23298             this.el.dom.style.display = "none";
23299             Roo.Shadow.Pool.push(this.el);
23300             delete this.el;
23301         }
23302     },
23303
23304     /**
23305      * Adjust the z-index of this shadow
23306      * @param {Number} zindex The new z-index
23307      */
23308     setZIndex : function(z){
23309         this.zIndex = z;
23310         if(this.el){
23311             this.el.setStyle("z-index", z);
23312         }
23313     }
23314 };
23315
23316 // Private utility class that manages the internal Shadow cache
23317 Roo.Shadow.Pool = function(){
23318     var p = [];
23319     var markup = Roo.isIE ?
23320                  '<div class="x-ie-shadow"></div>' :
23321                  '<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>';
23322     return {
23323         pull : function(){
23324             var sh = p.shift();
23325             if(!sh){
23326                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
23327                 sh.autoBoxAdjust = false;
23328             }
23329             return sh;
23330         },
23331
23332         push : function(sh){
23333             p.push(sh);
23334         }
23335     };
23336 }();/*
23337  * Based on:
23338  * Ext JS Library 1.1.1
23339  * Copyright(c) 2006-2007, Ext JS, LLC.
23340  *
23341  * Originally Released Under LGPL - original licence link has changed is not relivant.
23342  *
23343  * Fork - LGPL
23344  * <script type="text/javascript">
23345  */
23346
23347 /**
23348  * @class Roo.BoxComponent
23349  * @extends Roo.Component
23350  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
23351  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
23352  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
23353  * layout containers.
23354  * @constructor
23355  * @param {Roo.Element/String/Object} config The configuration options.
23356  */
23357 Roo.BoxComponent = function(config){
23358     Roo.Component.call(this, config);
23359     this.addEvents({
23360         /**
23361          * @event resize
23362          * Fires after the component is resized.
23363              * @param {Roo.Component} this
23364              * @param {Number} adjWidth The box-adjusted width that was set
23365              * @param {Number} adjHeight The box-adjusted height that was set
23366              * @param {Number} rawWidth The width that was originally specified
23367              * @param {Number} rawHeight The height that was originally specified
23368              */
23369         resize : true,
23370         /**
23371          * @event move
23372          * Fires after the component is moved.
23373              * @param {Roo.Component} this
23374              * @param {Number} x The new x position
23375              * @param {Number} y The new y position
23376              */
23377         move : true
23378     });
23379 };
23380
23381 Roo.extend(Roo.BoxComponent, Roo.Component, {
23382     // private, set in afterRender to signify that the component has been rendered
23383     boxReady : false,
23384     // private, used to defer height settings to subclasses
23385     deferHeight: false,
23386     /** @cfg {Number} width
23387      * width (optional) size of component
23388      */
23389      /** @cfg {Number} height
23390      * height (optional) size of component
23391      */
23392      
23393     /**
23394      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
23395      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
23396      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
23397      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
23398      * @return {Roo.BoxComponent} this
23399      */
23400     setSize : function(w, h){
23401         // support for standard size objects
23402         if(typeof w == 'object'){
23403             h = w.height;
23404             w = w.width;
23405         }
23406         // not rendered
23407         if(!this.boxReady){
23408             this.width = w;
23409             this.height = h;
23410             return this;
23411         }
23412
23413         // prevent recalcs when not needed
23414         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
23415             return this;
23416         }
23417         this.lastSize = {width: w, height: h};
23418
23419         var adj = this.adjustSize(w, h);
23420         var aw = adj.width, ah = adj.height;
23421         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
23422             var rz = this.getResizeEl();
23423             if(!this.deferHeight && aw !== undefined && ah !== undefined){
23424                 rz.setSize(aw, ah);
23425             }else if(!this.deferHeight && ah !== undefined){
23426                 rz.setHeight(ah);
23427             }else if(aw !== undefined){
23428                 rz.setWidth(aw);
23429             }
23430             this.onResize(aw, ah, w, h);
23431             this.fireEvent('resize', this, aw, ah, w, h);
23432         }
23433         return this;
23434     },
23435
23436     /**
23437      * Gets the current size of the component's underlying element.
23438      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
23439      */
23440     getSize : function(){
23441         return this.el.getSize();
23442     },
23443
23444     /**
23445      * Gets the current XY position of the component's underlying element.
23446      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23447      * @return {Array} The XY position of the element (e.g., [100, 200])
23448      */
23449     getPosition : function(local){
23450         if(local === true){
23451             return [this.el.getLeft(true), this.el.getTop(true)];
23452         }
23453         return this.xy || this.el.getXY();
23454     },
23455
23456     /**
23457      * Gets the current box measurements of the component's underlying element.
23458      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23459      * @returns {Object} box An object in the format {x, y, width, height}
23460      */
23461     getBox : function(local){
23462         var s = this.el.getSize();
23463         if(local){
23464             s.x = this.el.getLeft(true);
23465             s.y = this.el.getTop(true);
23466         }else{
23467             var xy = this.xy || this.el.getXY();
23468             s.x = xy[0];
23469             s.y = xy[1];
23470         }
23471         return s;
23472     },
23473
23474     /**
23475      * Sets the current box measurements of the component's underlying element.
23476      * @param {Object} box An object in the format {x, y, width, height}
23477      * @returns {Roo.BoxComponent} this
23478      */
23479     updateBox : function(box){
23480         this.setSize(box.width, box.height);
23481         this.setPagePosition(box.x, box.y);
23482         return this;
23483     },
23484
23485     // protected
23486     getResizeEl : function(){
23487         return this.resizeEl || this.el;
23488     },
23489
23490     // protected
23491     getPositionEl : function(){
23492         return this.positionEl || this.el;
23493     },
23494
23495     /**
23496      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23497      * This method fires the move event.
23498      * @param {Number} left The new left
23499      * @param {Number} top The new top
23500      * @returns {Roo.BoxComponent} this
23501      */
23502     setPosition : function(x, y){
23503         this.x = x;
23504         this.y = y;
23505         if(!this.boxReady){
23506             return this;
23507         }
23508         var adj = this.adjustPosition(x, y);
23509         var ax = adj.x, ay = adj.y;
23510
23511         var el = this.getPositionEl();
23512         if(ax !== undefined || ay !== undefined){
23513             if(ax !== undefined && ay !== undefined){
23514                 el.setLeftTop(ax, ay);
23515             }else if(ax !== undefined){
23516                 el.setLeft(ax);
23517             }else if(ay !== undefined){
23518                 el.setTop(ay);
23519             }
23520             this.onPosition(ax, ay);
23521             this.fireEvent('move', this, ax, ay);
23522         }
23523         return this;
23524     },
23525
23526     /**
23527      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23528      * This method fires the move event.
23529      * @param {Number} x The new x position
23530      * @param {Number} y The new y position
23531      * @returns {Roo.BoxComponent} this
23532      */
23533     setPagePosition : function(x, y){
23534         this.pageX = x;
23535         this.pageY = y;
23536         if(!this.boxReady){
23537             return;
23538         }
23539         if(x === undefined || y === undefined){ // cannot translate undefined points
23540             return;
23541         }
23542         var p = this.el.translatePoints(x, y);
23543         this.setPosition(p.left, p.top);
23544         return this;
23545     },
23546
23547     // private
23548     onRender : function(ct, position){
23549         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23550         if(this.resizeEl){
23551             this.resizeEl = Roo.get(this.resizeEl);
23552         }
23553         if(this.positionEl){
23554             this.positionEl = Roo.get(this.positionEl);
23555         }
23556     },
23557
23558     // private
23559     afterRender : function(){
23560         Roo.BoxComponent.superclass.afterRender.call(this);
23561         this.boxReady = true;
23562         this.setSize(this.width, this.height);
23563         if(this.x || this.y){
23564             this.setPosition(this.x, this.y);
23565         }
23566         if(this.pageX || this.pageY){
23567             this.setPagePosition(this.pageX, this.pageY);
23568         }
23569     },
23570
23571     /**
23572      * Force the component's size to recalculate based on the underlying element's current height and width.
23573      * @returns {Roo.BoxComponent} this
23574      */
23575     syncSize : function(){
23576         delete this.lastSize;
23577         this.setSize(this.el.getWidth(), this.el.getHeight());
23578         return this;
23579     },
23580
23581     /**
23582      * Called after the component is resized, this method is empty by default but can be implemented by any
23583      * subclass that needs to perform custom logic after a resize occurs.
23584      * @param {Number} adjWidth The box-adjusted width that was set
23585      * @param {Number} adjHeight The box-adjusted height that was set
23586      * @param {Number} rawWidth The width that was originally specified
23587      * @param {Number} rawHeight The height that was originally specified
23588      */
23589     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23590
23591     },
23592
23593     /**
23594      * Called after the component is moved, this method is empty by default but can be implemented by any
23595      * subclass that needs to perform custom logic after a move occurs.
23596      * @param {Number} x The new x position
23597      * @param {Number} y The new y position
23598      */
23599     onPosition : function(x, y){
23600
23601     },
23602
23603     // private
23604     adjustSize : function(w, h){
23605         if(this.autoWidth){
23606             w = 'auto';
23607         }
23608         if(this.autoHeight){
23609             h = 'auto';
23610         }
23611         return {width : w, height: h};
23612     },
23613
23614     // private
23615     adjustPosition : function(x, y){
23616         return {x : x, y: y};
23617     }
23618 });/*
23619  * Based on:
23620  * Ext JS Library 1.1.1
23621  * Copyright(c) 2006-2007, Ext JS, LLC.
23622  *
23623  * Originally Released Under LGPL - original licence link has changed is not relivant.
23624  *
23625  * Fork - LGPL
23626  * <script type="text/javascript">
23627  */
23628
23629
23630 /**
23631  * @class Roo.SplitBar
23632  * @extends Roo.util.Observable
23633  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23634  * <br><br>
23635  * Usage:
23636  * <pre><code>
23637 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23638                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23639 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23640 split.minSize = 100;
23641 split.maxSize = 600;
23642 split.animate = true;
23643 split.on('moved', splitterMoved);
23644 </code></pre>
23645  * @constructor
23646  * Create a new SplitBar
23647  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23648  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23649  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23650  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23651                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23652                         position of the SplitBar).
23653  */
23654 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23655     
23656     /** @private */
23657     this.el = Roo.get(dragElement, true);
23658     this.el.dom.unselectable = "on";
23659     /** @private */
23660     this.resizingEl = Roo.get(resizingElement, true);
23661
23662     /**
23663      * @private
23664      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23665      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23666      * @type Number
23667      */
23668     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23669     
23670     /**
23671      * The minimum size of the resizing element. (Defaults to 0)
23672      * @type Number
23673      */
23674     this.minSize = 0;
23675     
23676     /**
23677      * The maximum size of the resizing element. (Defaults to 2000)
23678      * @type Number
23679      */
23680     this.maxSize = 2000;
23681     
23682     /**
23683      * Whether to animate the transition to the new size
23684      * @type Boolean
23685      */
23686     this.animate = false;
23687     
23688     /**
23689      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23690      * @type Boolean
23691      */
23692     this.useShim = false;
23693     
23694     /** @private */
23695     this.shim = null;
23696     
23697     if(!existingProxy){
23698         /** @private */
23699         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23700     }else{
23701         this.proxy = Roo.get(existingProxy).dom;
23702     }
23703     /** @private */
23704     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23705     
23706     /** @private */
23707     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23708     
23709     /** @private */
23710     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23711     
23712     /** @private */
23713     this.dragSpecs = {};
23714     
23715     /**
23716      * @private The adapter to use to positon and resize elements
23717      */
23718     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23719     this.adapter.init(this);
23720     
23721     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23722         /** @private */
23723         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23724         this.el.addClass("x-splitbar-h");
23725     }else{
23726         /** @private */
23727         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23728         this.el.addClass("x-splitbar-v");
23729     }
23730     
23731     this.addEvents({
23732         /**
23733          * @event resize
23734          * Fires when the splitter is moved (alias for {@link #event-moved})
23735          * @param {Roo.SplitBar} this
23736          * @param {Number} newSize the new width or height
23737          */
23738         "resize" : true,
23739         /**
23740          * @event moved
23741          * Fires when the splitter is moved
23742          * @param {Roo.SplitBar} this
23743          * @param {Number} newSize the new width or height
23744          */
23745         "moved" : true,
23746         /**
23747          * @event beforeresize
23748          * Fires before the splitter is dragged
23749          * @param {Roo.SplitBar} this
23750          */
23751         "beforeresize" : true,
23752
23753         "beforeapply" : true
23754     });
23755
23756     Roo.util.Observable.call(this);
23757 };
23758
23759 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23760     onStartProxyDrag : function(x, y){
23761         this.fireEvent("beforeresize", this);
23762         if(!this.overlay){
23763             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23764             o.unselectable();
23765             o.enableDisplayMode("block");
23766             // all splitbars share the same overlay
23767             Roo.SplitBar.prototype.overlay = o;
23768         }
23769         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23770         this.overlay.show();
23771         Roo.get(this.proxy).setDisplayed("block");
23772         var size = this.adapter.getElementSize(this);
23773         this.activeMinSize = this.getMinimumSize();;
23774         this.activeMaxSize = this.getMaximumSize();;
23775         var c1 = size - this.activeMinSize;
23776         var c2 = Math.max(this.activeMaxSize - size, 0);
23777         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23778             this.dd.resetConstraints();
23779             this.dd.setXConstraint(
23780                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23781                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23782             );
23783             this.dd.setYConstraint(0, 0);
23784         }else{
23785             this.dd.resetConstraints();
23786             this.dd.setXConstraint(0, 0);
23787             this.dd.setYConstraint(
23788                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23789                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23790             );
23791          }
23792         this.dragSpecs.startSize = size;
23793         this.dragSpecs.startPoint = [x, y];
23794         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23795     },
23796     
23797     /** 
23798      * @private Called after the drag operation by the DDProxy
23799      */
23800     onEndProxyDrag : function(e){
23801         Roo.get(this.proxy).setDisplayed(false);
23802         var endPoint = Roo.lib.Event.getXY(e);
23803         if(this.overlay){
23804             this.overlay.hide();
23805         }
23806         var newSize;
23807         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23808             newSize = this.dragSpecs.startSize + 
23809                 (this.placement == Roo.SplitBar.LEFT ?
23810                     endPoint[0] - this.dragSpecs.startPoint[0] :
23811                     this.dragSpecs.startPoint[0] - endPoint[0]
23812                 );
23813         }else{
23814             newSize = this.dragSpecs.startSize + 
23815                 (this.placement == Roo.SplitBar.TOP ?
23816                     endPoint[1] - this.dragSpecs.startPoint[1] :
23817                     this.dragSpecs.startPoint[1] - endPoint[1]
23818                 );
23819         }
23820         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23821         if(newSize != this.dragSpecs.startSize){
23822             if(this.fireEvent('beforeapply', this, newSize) !== false){
23823                 this.adapter.setElementSize(this, newSize);
23824                 this.fireEvent("moved", this, newSize);
23825                 this.fireEvent("resize", this, newSize);
23826             }
23827         }
23828     },
23829     
23830     /**
23831      * Get the adapter this SplitBar uses
23832      * @return The adapter object
23833      */
23834     getAdapter : function(){
23835         return this.adapter;
23836     },
23837     
23838     /**
23839      * Set the adapter this SplitBar uses
23840      * @param {Object} adapter A SplitBar adapter object
23841      */
23842     setAdapter : function(adapter){
23843         this.adapter = adapter;
23844         this.adapter.init(this);
23845     },
23846     
23847     /**
23848      * Gets the minimum size for the resizing element
23849      * @return {Number} The minimum size
23850      */
23851     getMinimumSize : function(){
23852         return this.minSize;
23853     },
23854     
23855     /**
23856      * Sets the minimum size for the resizing element
23857      * @param {Number} minSize The minimum size
23858      */
23859     setMinimumSize : function(minSize){
23860         this.minSize = minSize;
23861     },
23862     
23863     /**
23864      * Gets the maximum size for the resizing element
23865      * @return {Number} The maximum size
23866      */
23867     getMaximumSize : function(){
23868         return this.maxSize;
23869     },
23870     
23871     /**
23872      * Sets the maximum size for the resizing element
23873      * @param {Number} maxSize The maximum size
23874      */
23875     setMaximumSize : function(maxSize){
23876         this.maxSize = maxSize;
23877     },
23878     
23879     /**
23880      * Sets the initialize size for the resizing element
23881      * @param {Number} size The initial size
23882      */
23883     setCurrentSize : function(size){
23884         var oldAnimate = this.animate;
23885         this.animate = false;
23886         this.adapter.setElementSize(this, size);
23887         this.animate = oldAnimate;
23888     },
23889     
23890     /**
23891      * Destroy this splitbar. 
23892      * @param {Boolean} removeEl True to remove the element
23893      */
23894     destroy : function(removeEl){
23895         if(this.shim){
23896             this.shim.remove();
23897         }
23898         this.dd.unreg();
23899         this.proxy.parentNode.removeChild(this.proxy);
23900         if(removeEl){
23901             this.el.remove();
23902         }
23903     }
23904 });
23905
23906 /**
23907  * @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.
23908  */
23909 Roo.SplitBar.createProxy = function(dir){
23910     var proxy = new Roo.Element(document.createElement("div"));
23911     proxy.unselectable();
23912     var cls = 'x-splitbar-proxy';
23913     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23914     document.body.appendChild(proxy.dom);
23915     return proxy.dom;
23916 };
23917
23918 /** 
23919  * @class Roo.SplitBar.BasicLayoutAdapter
23920  * Default Adapter. It assumes the splitter and resizing element are not positioned
23921  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23922  */
23923 Roo.SplitBar.BasicLayoutAdapter = function(){
23924 };
23925
23926 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23927     // do nothing for now
23928     init : function(s){
23929     
23930     },
23931     /**
23932      * Called before drag operations to get the current size of the resizing element. 
23933      * @param {Roo.SplitBar} s The SplitBar using this adapter
23934      */
23935      getElementSize : function(s){
23936         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23937             return s.resizingEl.getWidth();
23938         }else{
23939             return s.resizingEl.getHeight();
23940         }
23941     },
23942     
23943     /**
23944      * Called after drag operations to set the size of the resizing element.
23945      * @param {Roo.SplitBar} s The SplitBar using this adapter
23946      * @param {Number} newSize The new size to set
23947      * @param {Function} onComplete A function to be invoked when resizing is complete
23948      */
23949     setElementSize : function(s, newSize, onComplete){
23950         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23951             if(!s.animate){
23952                 s.resizingEl.setWidth(newSize);
23953                 if(onComplete){
23954                     onComplete(s, newSize);
23955                 }
23956             }else{
23957                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23958             }
23959         }else{
23960             
23961             if(!s.animate){
23962                 s.resizingEl.setHeight(newSize);
23963                 if(onComplete){
23964                     onComplete(s, newSize);
23965                 }
23966             }else{
23967                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23968             }
23969         }
23970     }
23971 };
23972
23973 /** 
23974  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23975  * @extends Roo.SplitBar.BasicLayoutAdapter
23976  * Adapter that  moves the splitter element to align with the resized sizing element. 
23977  * Used with an absolute positioned SplitBar.
23978  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23979  * document.body, make sure you assign an id to the body element.
23980  */
23981 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23982     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23983     this.container = Roo.get(container);
23984 };
23985
23986 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23987     init : function(s){
23988         this.basic.init(s);
23989     },
23990     
23991     getElementSize : function(s){
23992         return this.basic.getElementSize(s);
23993     },
23994     
23995     setElementSize : function(s, newSize, onComplete){
23996         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23997     },
23998     
23999     moveSplitter : function(s){
24000         var yes = Roo.SplitBar;
24001         switch(s.placement){
24002             case yes.LEFT:
24003                 s.el.setX(s.resizingEl.getRight());
24004                 break;
24005             case yes.RIGHT:
24006                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24007                 break;
24008             case yes.TOP:
24009                 s.el.setY(s.resizingEl.getBottom());
24010                 break;
24011             case yes.BOTTOM:
24012                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24013                 break;
24014         }
24015     }
24016 };
24017
24018 /**
24019  * Orientation constant - Create a vertical SplitBar
24020  * @static
24021  * @type Number
24022  */
24023 Roo.SplitBar.VERTICAL = 1;
24024
24025 /**
24026  * Orientation constant - Create a horizontal SplitBar
24027  * @static
24028  * @type Number
24029  */
24030 Roo.SplitBar.HORIZONTAL = 2;
24031
24032 /**
24033  * Placement constant - The resizing element is to the left of the splitter element
24034  * @static
24035  * @type Number
24036  */
24037 Roo.SplitBar.LEFT = 1;
24038
24039 /**
24040  * Placement constant - The resizing element is to the right of the splitter element
24041  * @static
24042  * @type Number
24043  */
24044 Roo.SplitBar.RIGHT = 2;
24045
24046 /**
24047  * Placement constant - The resizing element is positioned above the splitter element
24048  * @static
24049  * @type Number
24050  */
24051 Roo.SplitBar.TOP = 3;
24052
24053 /**
24054  * Placement constant - The resizing element is positioned under splitter element
24055  * @static
24056  * @type Number
24057  */
24058 Roo.SplitBar.BOTTOM = 4;
24059 /*
24060  * Based on:
24061  * Ext JS Library 1.1.1
24062  * Copyright(c) 2006-2007, Ext JS, LLC.
24063  *
24064  * Originally Released Under LGPL - original licence link has changed is not relivant.
24065  *
24066  * Fork - LGPL
24067  * <script type="text/javascript">
24068  */
24069
24070 /**
24071  * @class Roo.View
24072  * @extends Roo.util.Observable
24073  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24074  * This class also supports single and multi selection modes. <br>
24075  * Create a data model bound view:
24076  <pre><code>
24077  var store = new Roo.data.Store(...);
24078
24079  var view = new Roo.View({
24080     el : "my-element",
24081     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24082  
24083     singleSelect: true,
24084     selectedClass: "ydataview-selected",
24085     store: store
24086  });
24087
24088  // listen for node click?
24089  view.on("click", function(vw, index, node, e){
24090  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24091  });
24092
24093  // load XML data
24094  dataModel.load("foobar.xml");
24095  </code></pre>
24096  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24097  * <br><br>
24098  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24099  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24100  * 
24101  * Note: old style constructor is still suported (container, template, config)
24102  * 
24103  * @constructor
24104  * Create a new View
24105  * @param {Object} config The config object
24106  * 
24107  */
24108 Roo.View = function(config, depreciated_tpl, depreciated_config){
24109     
24110     if (typeof(depreciated_tpl) == 'undefined') {
24111         // new way.. - universal constructor.
24112         Roo.apply(this, config);
24113         this.el  = Roo.get(this.el);
24114     } else {
24115         // old format..
24116         this.el  = Roo.get(config);
24117         this.tpl = depreciated_tpl;
24118         Roo.apply(this, depreciated_config);
24119     }
24120      
24121     
24122     if(typeof(this.tpl) == "string"){
24123         this.tpl = new Roo.Template(this.tpl);
24124     } else {
24125         // support xtype ctors..
24126         this.tpl = new Roo.factory(this.tpl, Roo);
24127     }
24128     
24129     
24130     this.tpl.compile();
24131    
24132
24133      
24134     /** @private */
24135     this.addEvents({
24136         /**
24137          * @event beforeclick
24138          * Fires before a click is processed. Returns false to cancel the default action.
24139          * @param {Roo.View} this
24140          * @param {Number} index The index of the target node
24141          * @param {HTMLElement} node The target node
24142          * @param {Roo.EventObject} e The raw event object
24143          */
24144             "beforeclick" : true,
24145         /**
24146          * @event click
24147          * Fires when a template node is clicked.
24148          * @param {Roo.View} this
24149          * @param {Number} index The index of the target node
24150          * @param {HTMLElement} node The target node
24151          * @param {Roo.EventObject} e The raw event object
24152          */
24153             "click" : true,
24154         /**
24155          * @event dblclick
24156          * Fires when a template node is double clicked.
24157          * @param {Roo.View} this
24158          * @param {Number} index The index of the target node
24159          * @param {HTMLElement} node The target node
24160          * @param {Roo.EventObject} e The raw event object
24161          */
24162             "dblclick" : true,
24163         /**
24164          * @event contextmenu
24165          * Fires when a template node is right clicked.
24166          * @param {Roo.View} this
24167          * @param {Number} index The index of the target node
24168          * @param {HTMLElement} node The target node
24169          * @param {Roo.EventObject} e The raw event object
24170          */
24171             "contextmenu" : true,
24172         /**
24173          * @event selectionchange
24174          * Fires when the selected nodes change.
24175          * @param {Roo.View} this
24176          * @param {Array} selections Array of the selected nodes
24177          */
24178             "selectionchange" : true,
24179     
24180         /**
24181          * @event beforeselect
24182          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24183          * @param {Roo.View} this
24184          * @param {HTMLElement} node The node to be selected
24185          * @param {Array} selections Array of currently selected nodes
24186          */
24187             "beforeselect" : true,
24188         /**
24189          * @event preparedata
24190          * Fires on every row to render, to allow you to change the data.
24191          * @param {Roo.View} this
24192          * @param {Object} data to be rendered (change this)
24193          */
24194           "preparedata" : true
24195         });
24196
24197     this.el.on({
24198         "click": this.onClick,
24199         "dblclick": this.onDblClick,
24200         "contextmenu": this.onContextMenu,
24201         scope:this
24202     });
24203
24204     this.selections = [];
24205     this.nodes = [];
24206     this.cmp = new Roo.CompositeElementLite([]);
24207     if(this.store){
24208         this.store = Roo.factory(this.store, Roo.data);
24209         this.setStore(this.store, true);
24210     }
24211     Roo.View.superclass.constructor.call(this);
24212 };
24213
24214 Roo.extend(Roo.View, Roo.util.Observable, {
24215     
24216      /**
24217      * @cfg {Roo.data.Store} store Data store to load data from.
24218      */
24219     store : false,
24220     
24221     /**
24222      * @cfg {String|Roo.Element} el The container element.
24223      */
24224     el : '',
24225     
24226     /**
24227      * @cfg {String|Roo.Template} tpl The template used by this View 
24228      */
24229     tpl : false,
24230     /**
24231      * @cfg {String} dataName the named area of the template to use as the data area
24232      *                          Works with domtemplates roo-name="name"
24233      */
24234     dataName: false,
24235     /**
24236      * @cfg {String} selectedClass The css class to add to selected nodes
24237      */
24238     selectedClass : "x-view-selected",
24239      /**
24240      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24241      */
24242     emptyText : "",
24243     
24244     /**
24245      * @cfg {String} text to display on mask (default Loading)
24246      */
24247     mask : false,
24248     /**
24249      * @cfg {Boolean} multiSelect Allow multiple selection
24250      */
24251     multiSelect : false,
24252     /**
24253      * @cfg {Boolean} singleSelect Allow single selection
24254      */
24255     singleSelect:  false,
24256     
24257     /**
24258      * @cfg {Boolean} toggleSelect - selecting 
24259      */
24260     toggleSelect : false,
24261     
24262     /**
24263      * Returns the element this view is bound to.
24264      * @return {Roo.Element}
24265      */
24266     getEl : function(){
24267         return this.el;
24268     },
24269
24270     /**
24271      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24272      */
24273     refresh : function(){
24274         var t = this.tpl;
24275         
24276         // if we are using something like 'domtemplate', then
24277         // the what gets used is:
24278         // t.applySubtemplate(NAME, data, wrapping data..)
24279         // the outer template then get' applied with
24280         //     the store 'extra data'
24281         // and the body get's added to the
24282         //      roo-name="data" node?
24283         //      <span class='roo-tpl-{name}'></span> ?????
24284         
24285         
24286         
24287         this.clearSelections();
24288         this.el.update("");
24289         var html = [];
24290         var records = this.store.getRange();
24291         if(records.length < 1) {
24292             
24293             // is this valid??  = should it render a template??
24294             
24295             this.el.update(this.emptyText);
24296             return;
24297         }
24298         var el = this.el;
24299         if (this.dataName) {
24300             this.el.update(t.apply(this.store.meta)); //????
24301             el = this.el.child('.roo-tpl-' + this.dataName);
24302         }
24303         
24304         for(var i = 0, len = records.length; i < len; i++){
24305             var data = this.prepareData(records[i].data, i, records[i]);
24306             this.fireEvent("preparedata", this, data, i, records[i]);
24307             html[html.length] = Roo.util.Format.trim(
24308                 this.dataName ?
24309                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24310                     t.apply(data)
24311             );
24312         }
24313         
24314         
24315         
24316         el.update(html.join(""));
24317         this.nodes = el.dom.childNodes;
24318         this.updateIndexes(0);
24319     },
24320
24321     /**
24322      * Function to override to reformat the data that is sent to
24323      * the template for each node.
24324      * DEPRICATED - use the preparedata event handler.
24325      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24326      * a JSON object for an UpdateManager bound view).
24327      */
24328     prepareData : function(data, index, record)
24329     {
24330         this.fireEvent("preparedata", this, data, index, record);
24331         return data;
24332     },
24333
24334     onUpdate : function(ds, record){
24335         this.clearSelections();
24336         var index = this.store.indexOf(record);
24337         var n = this.nodes[index];
24338         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24339         n.parentNode.removeChild(n);
24340         this.updateIndexes(index, index);
24341     },
24342
24343     
24344     
24345 // --------- FIXME     
24346     onAdd : function(ds, records, index)
24347     {
24348         this.clearSelections();
24349         if(this.nodes.length == 0){
24350             this.refresh();
24351             return;
24352         }
24353         var n = this.nodes[index];
24354         for(var i = 0, len = records.length; i < len; i++){
24355             var d = this.prepareData(records[i].data, i, records[i]);
24356             if(n){
24357                 this.tpl.insertBefore(n, d);
24358             }else{
24359                 
24360                 this.tpl.append(this.el, d);
24361             }
24362         }
24363         this.updateIndexes(index);
24364     },
24365
24366     onRemove : function(ds, record, index){
24367         this.clearSelections();
24368         var el = this.dataName  ?
24369             this.el.child('.roo-tpl-' + this.dataName) :
24370             this.el; 
24371         el.dom.removeChild(this.nodes[index]);
24372         this.updateIndexes(index);
24373     },
24374
24375     /**
24376      * Refresh an individual node.
24377      * @param {Number} index
24378      */
24379     refreshNode : function(index){
24380         this.onUpdate(this.store, this.store.getAt(index));
24381     },
24382
24383     updateIndexes : function(startIndex, endIndex){
24384         var ns = this.nodes;
24385         startIndex = startIndex || 0;
24386         endIndex = endIndex || ns.length - 1;
24387         for(var i = startIndex; i <= endIndex; i++){
24388             ns[i].nodeIndex = i;
24389         }
24390     },
24391
24392     /**
24393      * Changes the data store this view uses and refresh the view.
24394      * @param {Store} store
24395      */
24396     setStore : function(store, initial){
24397         if(!initial && this.store){
24398             this.store.un("datachanged", this.refresh);
24399             this.store.un("add", this.onAdd);
24400             this.store.un("remove", this.onRemove);
24401             this.store.un("update", this.onUpdate);
24402             this.store.un("clear", this.refresh);
24403             this.store.un("beforeload", this.onBeforeLoad);
24404             this.store.un("load", this.onLoad);
24405             this.store.un("loadexception", this.onLoad);
24406         }
24407         if(store){
24408           
24409             store.on("datachanged", this.refresh, this);
24410             store.on("add", this.onAdd, this);
24411             store.on("remove", this.onRemove, this);
24412             store.on("update", this.onUpdate, this);
24413             store.on("clear", this.refresh, this);
24414             store.on("beforeload", this.onBeforeLoad, this);
24415             store.on("load", this.onLoad, this);
24416             store.on("loadexception", this.onLoad, this);
24417         }
24418         
24419         if(store){
24420             this.refresh();
24421         }
24422     },
24423     /**
24424      * onbeforeLoad - masks the loading area.
24425      *
24426      */
24427     onBeforeLoad : function()
24428     {
24429         this.el.update("");
24430         this.el.mask(this.mask ? this.mask : "Loading" ); 
24431     },
24432     onLoad : function ()
24433     {
24434         this.el.unmask();
24435     },
24436     
24437
24438     /**
24439      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
24440      * @param {HTMLElement} node
24441      * @return {HTMLElement} The template node
24442      */
24443     findItemFromChild : function(node){
24444         var el = this.dataName  ?
24445             this.el.child('.roo-tpl-' + this.dataName,true) :
24446             this.el.dom; 
24447         
24448         if(!node || node.parentNode == el){
24449                     return node;
24450             }
24451             var p = node.parentNode;
24452             while(p && p != el){
24453             if(p.parentNode == el){
24454                 return p;
24455             }
24456             p = p.parentNode;
24457         }
24458             return null;
24459     },
24460
24461     /** @ignore */
24462     onClick : function(e){
24463         var item = this.findItemFromChild(e.getTarget());
24464         if(item){
24465             var index = this.indexOf(item);
24466             if(this.onItemClick(item, index, e) !== false){
24467                 this.fireEvent("click", this, index, item, e);
24468             }
24469         }else{
24470             this.clearSelections();
24471         }
24472     },
24473
24474     /** @ignore */
24475     onContextMenu : function(e){
24476         var item = this.findItemFromChild(e.getTarget());
24477         if(item){
24478             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
24479         }
24480     },
24481
24482     /** @ignore */
24483     onDblClick : function(e){
24484         var item = this.findItemFromChild(e.getTarget());
24485         if(item){
24486             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
24487         }
24488     },
24489
24490     onItemClick : function(item, index, e)
24491     {
24492         if(this.fireEvent("beforeclick", this, index, item, e) === false){
24493             return false;
24494         }
24495         if (this.toggleSelect) {
24496             var m = this.isSelected(item) ? 'unselect' : 'select';
24497             Roo.log(m);
24498             var _t = this;
24499             _t[m](item, true, false);
24500             return true;
24501         }
24502         if(this.multiSelect || this.singleSelect){
24503             if(this.multiSelect && e.shiftKey && this.lastSelection){
24504                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
24505             }else{
24506                 this.select(item, this.multiSelect && e.ctrlKey);
24507                 this.lastSelection = item;
24508             }
24509             e.preventDefault();
24510         }
24511         return true;
24512     },
24513
24514     /**
24515      * Get the number of selected nodes.
24516      * @return {Number}
24517      */
24518     getSelectionCount : function(){
24519         return this.selections.length;
24520     },
24521
24522     /**
24523      * Get the currently selected nodes.
24524      * @return {Array} An array of HTMLElements
24525      */
24526     getSelectedNodes : function(){
24527         return this.selections;
24528     },
24529
24530     /**
24531      * Get the indexes of the selected nodes.
24532      * @return {Array}
24533      */
24534     getSelectedIndexes : function(){
24535         var indexes = [], s = this.selections;
24536         for(var i = 0, len = s.length; i < len; i++){
24537             indexes.push(s[i].nodeIndex);
24538         }
24539         return indexes;
24540     },
24541
24542     /**
24543      * Clear all selections
24544      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
24545      */
24546     clearSelections : function(suppressEvent){
24547         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24548             this.cmp.elements = this.selections;
24549             this.cmp.removeClass(this.selectedClass);
24550             this.selections = [];
24551             if(!suppressEvent){
24552                 this.fireEvent("selectionchange", this, this.selections);
24553             }
24554         }
24555     },
24556
24557     /**
24558      * Returns true if the passed node is selected
24559      * @param {HTMLElement/Number} node The node or node index
24560      * @return {Boolean}
24561      */
24562     isSelected : function(node){
24563         var s = this.selections;
24564         if(s.length < 1){
24565             return false;
24566         }
24567         node = this.getNode(node);
24568         return s.indexOf(node) !== -1;
24569     },
24570
24571     /**
24572      * Selects nodes.
24573      * @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
24574      * @param {Boolean} keepExisting (optional) true to keep existing selections
24575      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24576      */
24577     select : function(nodeInfo, keepExisting, suppressEvent){
24578         if(nodeInfo instanceof Array){
24579             if(!keepExisting){
24580                 this.clearSelections(true);
24581             }
24582             for(var i = 0, len = nodeInfo.length; i < len; i++){
24583                 this.select(nodeInfo[i], true, true);
24584             }
24585             return;
24586         } 
24587         var node = this.getNode(nodeInfo);
24588         if(!node || this.isSelected(node)){
24589             return; // already selected.
24590         }
24591         if(!keepExisting){
24592             this.clearSelections(true);
24593         }
24594         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24595             Roo.fly(node).addClass(this.selectedClass);
24596             this.selections.push(node);
24597             if(!suppressEvent){
24598                 this.fireEvent("selectionchange", this, this.selections);
24599             }
24600         }
24601         
24602         
24603     },
24604       /**
24605      * Unselects nodes.
24606      * @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
24607      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24608      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24609      */
24610     unselect : function(nodeInfo, keepExisting, suppressEvent)
24611     {
24612         if(nodeInfo instanceof Array){
24613             Roo.each(this.selections, function(s) {
24614                 this.unselect(s, nodeInfo);
24615             }, this);
24616             return;
24617         }
24618         var node = this.getNode(nodeInfo);
24619         if(!node || !this.isSelected(node)){
24620             Roo.log("not selected");
24621             return; // not selected.
24622         }
24623         // fireevent???
24624         var ns = [];
24625         Roo.each(this.selections, function(s) {
24626             if (s == node ) {
24627                 Roo.fly(node).removeClass(this.selectedClass);
24628
24629                 return;
24630             }
24631             ns.push(s);
24632         },this);
24633         
24634         this.selections= ns;
24635         this.fireEvent("selectionchange", this, this.selections);
24636     },
24637
24638     /**
24639      * Gets a template node.
24640      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24641      * @return {HTMLElement} The node or null if it wasn't found
24642      */
24643     getNode : function(nodeInfo){
24644         if(typeof nodeInfo == "string"){
24645             return document.getElementById(nodeInfo);
24646         }else if(typeof nodeInfo == "number"){
24647             return this.nodes[nodeInfo];
24648         }
24649         return nodeInfo;
24650     },
24651
24652     /**
24653      * Gets a range template nodes.
24654      * @param {Number} startIndex
24655      * @param {Number} endIndex
24656      * @return {Array} An array of nodes
24657      */
24658     getNodes : function(start, end){
24659         var ns = this.nodes;
24660         start = start || 0;
24661         end = typeof end == "undefined" ? ns.length - 1 : end;
24662         var nodes = [];
24663         if(start <= end){
24664             for(var i = start; i <= end; i++){
24665                 nodes.push(ns[i]);
24666             }
24667         } else{
24668             for(var i = start; i >= end; i--){
24669                 nodes.push(ns[i]);
24670             }
24671         }
24672         return nodes;
24673     },
24674
24675     /**
24676      * Finds the index of the passed node
24677      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24678      * @return {Number} The index of the node or -1
24679      */
24680     indexOf : function(node){
24681         node = this.getNode(node);
24682         if(typeof node.nodeIndex == "number"){
24683             return node.nodeIndex;
24684         }
24685         var ns = this.nodes;
24686         for(var i = 0, len = ns.length; i < len; i++){
24687             if(ns[i] == node){
24688                 return i;
24689             }
24690         }
24691         return -1;
24692     }
24693 });
24694 /*
24695  * Based on:
24696  * Ext JS Library 1.1.1
24697  * Copyright(c) 2006-2007, Ext JS, LLC.
24698  *
24699  * Originally Released Under LGPL - original licence link has changed is not relivant.
24700  *
24701  * Fork - LGPL
24702  * <script type="text/javascript">
24703  */
24704
24705 /**
24706  * @class Roo.JsonView
24707  * @extends Roo.View
24708  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24709 <pre><code>
24710 var view = new Roo.JsonView({
24711     container: "my-element",
24712     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24713     multiSelect: true, 
24714     jsonRoot: "data" 
24715 });
24716
24717 // listen for node click?
24718 view.on("click", function(vw, index, node, e){
24719     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24720 });
24721
24722 // direct load of JSON data
24723 view.load("foobar.php");
24724
24725 // Example from my blog list
24726 var tpl = new Roo.Template(
24727     '&lt;div class="entry"&gt;' +
24728     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24729     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24730     "&lt;/div&gt;&lt;hr /&gt;"
24731 );
24732
24733 var moreView = new Roo.JsonView({
24734     container :  "entry-list", 
24735     template : tpl,
24736     jsonRoot: "posts"
24737 });
24738 moreView.on("beforerender", this.sortEntries, this);
24739 moreView.load({
24740     url: "/blog/get-posts.php",
24741     params: "allposts=true",
24742     text: "Loading Blog Entries..."
24743 });
24744 </code></pre>
24745
24746 * Note: old code is supported with arguments : (container, template, config)
24747
24748
24749  * @constructor
24750  * Create a new JsonView
24751  * 
24752  * @param {Object} config The config object
24753  * 
24754  */
24755 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24756     
24757     
24758     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24759
24760     var um = this.el.getUpdateManager();
24761     um.setRenderer(this);
24762     um.on("update", this.onLoad, this);
24763     um.on("failure", this.onLoadException, this);
24764
24765     /**
24766      * @event beforerender
24767      * Fires before rendering of the downloaded JSON data.
24768      * @param {Roo.JsonView} this
24769      * @param {Object} data The JSON data loaded
24770      */
24771     /**
24772      * @event load
24773      * Fires when data is loaded.
24774      * @param {Roo.JsonView} this
24775      * @param {Object} data The JSON data loaded
24776      * @param {Object} response The raw Connect response object
24777      */
24778     /**
24779      * @event loadexception
24780      * Fires when loading fails.
24781      * @param {Roo.JsonView} this
24782      * @param {Object} response The raw Connect response object
24783      */
24784     this.addEvents({
24785         'beforerender' : true,
24786         'load' : true,
24787         'loadexception' : true
24788     });
24789 };
24790 Roo.extend(Roo.JsonView, Roo.View, {
24791     /**
24792      * @type {String} The root property in the loaded JSON object that contains the data
24793      */
24794     jsonRoot : "",
24795
24796     /**
24797      * Refreshes the view.
24798      */
24799     refresh : function(){
24800         this.clearSelections();
24801         this.el.update("");
24802         var html = [];
24803         var o = this.jsonData;
24804         if(o && o.length > 0){
24805             for(var i = 0, len = o.length; i < len; i++){
24806                 var data = this.prepareData(o[i], i, o);
24807                 html[html.length] = this.tpl.apply(data);
24808             }
24809         }else{
24810             html.push(this.emptyText);
24811         }
24812         this.el.update(html.join(""));
24813         this.nodes = this.el.dom.childNodes;
24814         this.updateIndexes(0);
24815     },
24816
24817     /**
24818      * 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.
24819      * @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:
24820      <pre><code>
24821      view.load({
24822          url: "your-url.php",
24823          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24824          callback: yourFunction,
24825          scope: yourObject, //(optional scope)
24826          discardUrl: false,
24827          nocache: false,
24828          text: "Loading...",
24829          timeout: 30,
24830          scripts: false
24831      });
24832      </code></pre>
24833      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24834      * 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.
24835      * @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}
24836      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24837      * @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.
24838      */
24839     load : function(){
24840         var um = this.el.getUpdateManager();
24841         um.update.apply(um, arguments);
24842     },
24843
24844     render : function(el, response){
24845         this.clearSelections();
24846         this.el.update("");
24847         var o;
24848         try{
24849             o = Roo.util.JSON.decode(response.responseText);
24850             if(this.jsonRoot){
24851                 
24852                 o = o[this.jsonRoot];
24853             }
24854         } catch(e){
24855         }
24856         /**
24857          * The current JSON data or null
24858          */
24859         this.jsonData = o;
24860         this.beforeRender();
24861         this.refresh();
24862     },
24863
24864 /**
24865  * Get the number of records in the current JSON dataset
24866  * @return {Number}
24867  */
24868     getCount : function(){
24869         return this.jsonData ? this.jsonData.length : 0;
24870     },
24871
24872 /**
24873  * Returns the JSON object for the specified node(s)
24874  * @param {HTMLElement/Array} node The node or an array of nodes
24875  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24876  * you get the JSON object for the node
24877  */
24878     getNodeData : function(node){
24879         if(node instanceof Array){
24880             var data = [];
24881             for(var i = 0, len = node.length; i < len; i++){
24882                 data.push(this.getNodeData(node[i]));
24883             }
24884             return data;
24885         }
24886         return this.jsonData[this.indexOf(node)] || null;
24887     },
24888
24889     beforeRender : function(){
24890         this.snapshot = this.jsonData;
24891         if(this.sortInfo){
24892             this.sort.apply(this, this.sortInfo);
24893         }
24894         this.fireEvent("beforerender", this, this.jsonData);
24895     },
24896
24897     onLoad : function(el, o){
24898         this.fireEvent("load", this, this.jsonData, o);
24899     },
24900
24901     onLoadException : function(el, o){
24902         this.fireEvent("loadexception", this, o);
24903     },
24904
24905 /**
24906  * Filter the data by a specific property.
24907  * @param {String} property A property on your JSON objects
24908  * @param {String/RegExp} value Either string that the property values
24909  * should start with, or a RegExp to test against the property
24910  */
24911     filter : function(property, value){
24912         if(this.jsonData){
24913             var data = [];
24914             var ss = this.snapshot;
24915             if(typeof value == "string"){
24916                 var vlen = value.length;
24917                 if(vlen == 0){
24918                     this.clearFilter();
24919                     return;
24920                 }
24921                 value = value.toLowerCase();
24922                 for(var i = 0, len = ss.length; i < len; i++){
24923                     var o = ss[i];
24924                     if(o[property].substr(0, vlen).toLowerCase() == value){
24925                         data.push(o);
24926                     }
24927                 }
24928             } else if(value.exec){ // regex?
24929                 for(var i = 0, len = ss.length; i < len; i++){
24930                     var o = ss[i];
24931                     if(value.test(o[property])){
24932                         data.push(o);
24933                     }
24934                 }
24935             } else{
24936                 return;
24937             }
24938             this.jsonData = data;
24939             this.refresh();
24940         }
24941     },
24942
24943 /**
24944  * Filter by a function. The passed function will be called with each
24945  * object in the current dataset. If the function returns true the value is kept,
24946  * otherwise it is filtered.
24947  * @param {Function} fn
24948  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24949  */
24950     filterBy : function(fn, scope){
24951         if(this.jsonData){
24952             var data = [];
24953             var ss = this.snapshot;
24954             for(var i = 0, len = ss.length; i < len; i++){
24955                 var o = ss[i];
24956                 if(fn.call(scope || this, o)){
24957                     data.push(o);
24958                 }
24959             }
24960             this.jsonData = data;
24961             this.refresh();
24962         }
24963     },
24964
24965 /**
24966  * Clears the current filter.
24967  */
24968     clearFilter : function(){
24969         if(this.snapshot && this.jsonData != this.snapshot){
24970             this.jsonData = this.snapshot;
24971             this.refresh();
24972         }
24973     },
24974
24975
24976 /**
24977  * Sorts the data for this view and refreshes it.
24978  * @param {String} property A property on your JSON objects to sort on
24979  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24980  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24981  */
24982     sort : function(property, dir, sortType){
24983         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24984         if(this.jsonData){
24985             var p = property;
24986             var dsc = dir && dir.toLowerCase() == "desc";
24987             var f = function(o1, o2){
24988                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24989                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24990                 ;
24991                 if(v1 < v2){
24992                     return dsc ? +1 : -1;
24993                 } else if(v1 > v2){
24994                     return dsc ? -1 : +1;
24995                 } else{
24996                     return 0;
24997                 }
24998             };
24999             this.jsonData.sort(f);
25000             this.refresh();
25001             if(this.jsonData != this.snapshot){
25002                 this.snapshot.sort(f);
25003             }
25004         }
25005     }
25006 });/*
25007  * Based on:
25008  * Ext JS Library 1.1.1
25009  * Copyright(c) 2006-2007, Ext JS, LLC.
25010  *
25011  * Originally Released Under LGPL - original licence link has changed is not relivant.
25012  *
25013  * Fork - LGPL
25014  * <script type="text/javascript">
25015  */
25016  
25017
25018 /**
25019  * @class Roo.ColorPalette
25020  * @extends Roo.Component
25021  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25022  * Here's an example of typical usage:
25023  * <pre><code>
25024 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25025 cp.render('my-div');
25026
25027 cp.on('select', function(palette, selColor){
25028     // do something with selColor
25029 });
25030 </code></pre>
25031  * @constructor
25032  * Create a new ColorPalette
25033  * @param {Object} config The config object
25034  */
25035 Roo.ColorPalette = function(config){
25036     Roo.ColorPalette.superclass.constructor.call(this, config);
25037     this.addEvents({
25038         /**
25039              * @event select
25040              * Fires when a color is selected
25041              * @param {ColorPalette} this
25042              * @param {String} color The 6-digit color hex code (without the # symbol)
25043              */
25044         select: true
25045     });
25046
25047     if(this.handler){
25048         this.on("select", this.handler, this.scope, true);
25049     }
25050 };
25051 Roo.extend(Roo.ColorPalette, Roo.Component, {
25052     /**
25053      * @cfg {String} itemCls
25054      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25055      */
25056     itemCls : "x-color-palette",
25057     /**
25058      * @cfg {String} value
25059      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25060      * the hex codes are case-sensitive.
25061      */
25062     value : null,
25063     clickEvent:'click',
25064     // private
25065     ctype: "Roo.ColorPalette",
25066
25067     /**
25068      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25069      */
25070     allowReselect : false,
25071
25072     /**
25073      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25074      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25075      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25076      * of colors with the width setting until the box is symmetrical.</p>
25077      * <p>You can override individual colors if needed:</p>
25078      * <pre><code>
25079 var cp = new Roo.ColorPalette();
25080 cp.colors[0] = "FF0000";  // change the first box to red
25081 </code></pre>
25082
25083 Or you can provide a custom array of your own for complete control:
25084 <pre><code>
25085 var cp = new Roo.ColorPalette();
25086 cp.colors = ["000000", "993300", "333300"];
25087 </code></pre>
25088      * @type Array
25089      */
25090     colors : [
25091         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25092         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25093         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25094         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25095         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25096     ],
25097
25098     // private
25099     onRender : function(container, position){
25100         var t = new Roo.MasterTemplate(
25101             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25102         );
25103         var c = this.colors;
25104         for(var i = 0, len = c.length; i < len; i++){
25105             t.add([c[i]]);
25106         }
25107         var el = document.createElement("div");
25108         el.className = this.itemCls;
25109         t.overwrite(el);
25110         container.dom.insertBefore(el, position);
25111         this.el = Roo.get(el);
25112         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25113         if(this.clickEvent != 'click'){
25114             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25115         }
25116     },
25117
25118     // private
25119     afterRender : function(){
25120         Roo.ColorPalette.superclass.afterRender.call(this);
25121         if(this.value){
25122             var s = this.value;
25123             this.value = null;
25124             this.select(s);
25125         }
25126     },
25127
25128     // private
25129     handleClick : function(e, t){
25130         e.preventDefault();
25131         if(!this.disabled){
25132             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25133             this.select(c.toUpperCase());
25134         }
25135     },
25136
25137     /**
25138      * Selects the specified color in the palette (fires the select event)
25139      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25140      */
25141     select : function(color){
25142         color = color.replace("#", "");
25143         if(color != this.value || this.allowReselect){
25144             var el = this.el;
25145             if(this.value){
25146                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25147             }
25148             el.child("a.color-"+color).addClass("x-color-palette-sel");
25149             this.value = color;
25150             this.fireEvent("select", this, color);
25151         }
25152     }
25153 });/*
25154  * Based on:
25155  * Ext JS Library 1.1.1
25156  * Copyright(c) 2006-2007, Ext JS, LLC.
25157  *
25158  * Originally Released Under LGPL - original licence link has changed is not relivant.
25159  *
25160  * Fork - LGPL
25161  * <script type="text/javascript">
25162  */
25163  
25164 /**
25165  * @class Roo.DatePicker
25166  * @extends Roo.Component
25167  * Simple date picker class.
25168  * @constructor
25169  * Create a new DatePicker
25170  * @param {Object} config The config object
25171  */
25172 Roo.DatePicker = function(config){
25173     Roo.DatePicker.superclass.constructor.call(this, config);
25174
25175     this.value = config && config.value ?
25176                  config.value.clearTime() : new Date().clearTime();
25177
25178     this.addEvents({
25179         /**
25180              * @event select
25181              * Fires when a date is selected
25182              * @param {DatePicker} this
25183              * @param {Date} date The selected date
25184              */
25185         'select': true,
25186         /**
25187              * @event monthchange
25188              * Fires when the displayed month changes 
25189              * @param {DatePicker} this
25190              * @param {Date} date The selected month
25191              */
25192         'monthchange': true
25193     });
25194
25195     if(this.handler){
25196         this.on("select", this.handler,  this.scope || this);
25197     }
25198     // build the disabledDatesRE
25199     if(!this.disabledDatesRE && this.disabledDates){
25200         var dd = this.disabledDates;
25201         var re = "(?:";
25202         for(var i = 0; i < dd.length; i++){
25203             re += dd[i];
25204             if(i != dd.length-1) re += "|";
25205         }
25206         this.disabledDatesRE = new RegExp(re + ")");
25207     }
25208 };
25209
25210 Roo.extend(Roo.DatePicker, Roo.Component, {
25211     /**
25212      * @cfg {String} todayText
25213      * The text to display on the button that selects the current date (defaults to "Today")
25214      */
25215     todayText : "Today",
25216     /**
25217      * @cfg {String} okText
25218      * The text to display on the ok button
25219      */
25220     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25221     /**
25222      * @cfg {String} cancelText
25223      * The text to display on the cancel button
25224      */
25225     cancelText : "Cancel",
25226     /**
25227      * @cfg {String} todayTip
25228      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25229      */
25230     todayTip : "{0} (Spacebar)",
25231     /**
25232      * @cfg {Date} minDate
25233      * Minimum allowable date (JavaScript date object, defaults to null)
25234      */
25235     minDate : null,
25236     /**
25237      * @cfg {Date} maxDate
25238      * Maximum allowable date (JavaScript date object, defaults to null)
25239      */
25240     maxDate : null,
25241     /**
25242      * @cfg {String} minText
25243      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25244      */
25245     minText : "This date is before the minimum date",
25246     /**
25247      * @cfg {String} maxText
25248      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25249      */
25250     maxText : "This date is after the maximum date",
25251     /**
25252      * @cfg {String} format
25253      * The default date format string which can be overriden for localization support.  The format must be
25254      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25255      */
25256     format : "m/d/y",
25257     /**
25258      * @cfg {Array} disabledDays
25259      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25260      */
25261     disabledDays : null,
25262     /**
25263      * @cfg {String} disabledDaysText
25264      * The tooltip to display when the date falls on a disabled day (defaults to "")
25265      */
25266     disabledDaysText : "",
25267     /**
25268      * @cfg {RegExp} disabledDatesRE
25269      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25270      */
25271     disabledDatesRE : null,
25272     /**
25273      * @cfg {String} disabledDatesText
25274      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25275      */
25276     disabledDatesText : "",
25277     /**
25278      * @cfg {Boolean} constrainToViewport
25279      * True to constrain the date picker to the viewport (defaults to true)
25280      */
25281     constrainToViewport : true,
25282     /**
25283      * @cfg {Array} monthNames
25284      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25285      */
25286     monthNames : Date.monthNames,
25287     /**
25288      * @cfg {Array} dayNames
25289      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25290      */
25291     dayNames : Date.dayNames,
25292     /**
25293      * @cfg {String} nextText
25294      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25295      */
25296     nextText: 'Next Month (Control+Right)',
25297     /**
25298      * @cfg {String} prevText
25299      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25300      */
25301     prevText: 'Previous Month (Control+Left)',
25302     /**
25303      * @cfg {String} monthYearText
25304      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25305      */
25306     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25307     /**
25308      * @cfg {Number} startDay
25309      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25310      */
25311     startDay : 0,
25312     /**
25313      * @cfg {Bool} showClear
25314      * Show a clear button (usefull for date form elements that can be blank.)
25315      */
25316     
25317     showClear: false,
25318     
25319     /**
25320      * Sets the value of the date field
25321      * @param {Date} value The date to set
25322      */
25323     setValue : function(value){
25324         var old = this.value;
25325         
25326         if (typeof(value) == 'string') {
25327          
25328             value = Date.parseDate(value, this.format);
25329         }
25330         if (!value) {
25331             value = new Date();
25332         }
25333         
25334         this.value = value.clearTime(true);
25335         if(this.el){
25336             this.update(this.value);
25337         }
25338     },
25339
25340     /**
25341      * Gets the current selected value of the date field
25342      * @return {Date} The selected date
25343      */
25344     getValue : function(){
25345         return this.value;
25346     },
25347
25348     // private
25349     focus : function(){
25350         if(this.el){
25351             this.update(this.activeDate);
25352         }
25353     },
25354
25355     // privateval
25356     onRender : function(container, position){
25357         
25358         var m = [
25359              '<table cellspacing="0">',
25360                 '<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>',
25361                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
25362         var dn = this.dayNames;
25363         for(var i = 0; i < 7; i++){
25364             var d = this.startDay+i;
25365             if(d > 6){
25366                 d = d-7;
25367             }
25368             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
25369         }
25370         m[m.length] = "</tr></thead><tbody><tr>";
25371         for(var i = 0; i < 42; i++) {
25372             if(i % 7 == 0 && i != 0){
25373                 m[m.length] = "</tr><tr>";
25374             }
25375             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
25376         }
25377         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
25378             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
25379
25380         var el = document.createElement("div");
25381         el.className = "x-date-picker";
25382         el.innerHTML = m.join("");
25383
25384         container.dom.insertBefore(el, position);
25385
25386         this.el = Roo.get(el);
25387         this.eventEl = Roo.get(el.firstChild);
25388
25389         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
25390             handler: this.showPrevMonth,
25391             scope: this,
25392             preventDefault:true,
25393             stopDefault:true
25394         });
25395
25396         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
25397             handler: this.showNextMonth,
25398             scope: this,
25399             preventDefault:true,
25400             stopDefault:true
25401         });
25402
25403         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
25404
25405         this.monthPicker = this.el.down('div.x-date-mp');
25406         this.monthPicker.enableDisplayMode('block');
25407         
25408         var kn = new Roo.KeyNav(this.eventEl, {
25409             "left" : function(e){
25410                 e.ctrlKey ?
25411                     this.showPrevMonth() :
25412                     this.update(this.activeDate.add("d", -1));
25413             },
25414
25415             "right" : function(e){
25416                 e.ctrlKey ?
25417                     this.showNextMonth() :
25418                     this.update(this.activeDate.add("d", 1));
25419             },
25420
25421             "up" : function(e){
25422                 e.ctrlKey ?
25423                     this.showNextYear() :
25424                     this.update(this.activeDate.add("d", -7));
25425             },
25426
25427             "down" : function(e){
25428                 e.ctrlKey ?
25429                     this.showPrevYear() :
25430                     this.update(this.activeDate.add("d", 7));
25431             },
25432
25433             "pageUp" : function(e){
25434                 this.showNextMonth();
25435             },
25436
25437             "pageDown" : function(e){
25438                 this.showPrevMonth();
25439             },
25440
25441             "enter" : function(e){
25442                 e.stopPropagation();
25443                 return true;
25444             },
25445
25446             scope : this
25447         });
25448
25449         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
25450
25451         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
25452
25453         this.el.unselectable();
25454         
25455         this.cells = this.el.select("table.x-date-inner tbody td");
25456         this.textNodes = this.el.query("table.x-date-inner tbody span");
25457
25458         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
25459             text: "&#160;",
25460             tooltip: this.monthYearText
25461         });
25462
25463         this.mbtn.on('click', this.showMonthPicker, this);
25464         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
25465
25466
25467         var today = (new Date()).dateFormat(this.format);
25468         
25469         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
25470         if (this.showClear) {
25471             baseTb.add( new Roo.Toolbar.Fill());
25472         }
25473         baseTb.add({
25474             text: String.format(this.todayText, today),
25475             tooltip: String.format(this.todayTip, today),
25476             handler: this.selectToday,
25477             scope: this
25478         });
25479         
25480         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
25481             
25482         //});
25483         if (this.showClear) {
25484             
25485             baseTb.add( new Roo.Toolbar.Fill());
25486             baseTb.add({
25487                 text: '&#160;',
25488                 cls: 'x-btn-icon x-btn-clear',
25489                 handler: function() {
25490                     //this.value = '';
25491                     this.fireEvent("select", this, '');
25492                 },
25493                 scope: this
25494             });
25495         }
25496         
25497         
25498         if(Roo.isIE){
25499             this.el.repaint();
25500         }
25501         this.update(this.value);
25502     },
25503
25504     createMonthPicker : function(){
25505         if(!this.monthPicker.dom.firstChild){
25506             var buf = ['<table border="0" cellspacing="0">'];
25507             for(var i = 0; i < 6; i++){
25508                 buf.push(
25509                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
25510                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
25511                     i == 0 ?
25512                     '<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>' :
25513                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
25514                 );
25515             }
25516             buf.push(
25517                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
25518                     this.okText,
25519                     '</button><button type="button" class="x-date-mp-cancel">',
25520                     this.cancelText,
25521                     '</button></td></tr>',
25522                 '</table>'
25523             );
25524             this.monthPicker.update(buf.join(''));
25525             this.monthPicker.on('click', this.onMonthClick, this);
25526             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
25527
25528             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
25529             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
25530
25531             this.mpMonths.each(function(m, a, i){
25532                 i += 1;
25533                 if((i%2) == 0){
25534                     m.dom.xmonth = 5 + Math.round(i * .5);
25535                 }else{
25536                     m.dom.xmonth = Math.round((i-1) * .5);
25537                 }
25538             });
25539         }
25540     },
25541
25542     showMonthPicker : function(){
25543         this.createMonthPicker();
25544         var size = this.el.getSize();
25545         this.monthPicker.setSize(size);
25546         this.monthPicker.child('table').setSize(size);
25547
25548         this.mpSelMonth = (this.activeDate || this.value).getMonth();
25549         this.updateMPMonth(this.mpSelMonth);
25550         this.mpSelYear = (this.activeDate || this.value).getFullYear();
25551         this.updateMPYear(this.mpSelYear);
25552
25553         this.monthPicker.slideIn('t', {duration:.2});
25554     },
25555
25556     updateMPYear : function(y){
25557         this.mpyear = y;
25558         var ys = this.mpYears.elements;
25559         for(var i = 1; i <= 10; i++){
25560             var td = ys[i-1], y2;
25561             if((i%2) == 0){
25562                 y2 = y + Math.round(i * .5);
25563                 td.firstChild.innerHTML = y2;
25564                 td.xyear = y2;
25565             }else{
25566                 y2 = y - (5-Math.round(i * .5));
25567                 td.firstChild.innerHTML = y2;
25568                 td.xyear = y2;
25569             }
25570             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25571         }
25572     },
25573
25574     updateMPMonth : function(sm){
25575         this.mpMonths.each(function(m, a, i){
25576             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25577         });
25578     },
25579
25580     selectMPMonth: function(m){
25581         
25582     },
25583
25584     onMonthClick : function(e, t){
25585         e.stopEvent();
25586         var el = new Roo.Element(t), pn;
25587         if(el.is('button.x-date-mp-cancel')){
25588             this.hideMonthPicker();
25589         }
25590         else if(el.is('button.x-date-mp-ok')){
25591             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25592             this.hideMonthPicker();
25593         }
25594         else if(pn = el.up('td.x-date-mp-month', 2)){
25595             this.mpMonths.removeClass('x-date-mp-sel');
25596             pn.addClass('x-date-mp-sel');
25597             this.mpSelMonth = pn.dom.xmonth;
25598         }
25599         else if(pn = el.up('td.x-date-mp-year', 2)){
25600             this.mpYears.removeClass('x-date-mp-sel');
25601             pn.addClass('x-date-mp-sel');
25602             this.mpSelYear = pn.dom.xyear;
25603         }
25604         else if(el.is('a.x-date-mp-prev')){
25605             this.updateMPYear(this.mpyear-10);
25606         }
25607         else if(el.is('a.x-date-mp-next')){
25608             this.updateMPYear(this.mpyear+10);
25609         }
25610     },
25611
25612     onMonthDblClick : function(e, t){
25613         e.stopEvent();
25614         var el = new Roo.Element(t), pn;
25615         if(pn = el.up('td.x-date-mp-month', 2)){
25616             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25617             this.hideMonthPicker();
25618         }
25619         else if(pn = el.up('td.x-date-mp-year', 2)){
25620             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25621             this.hideMonthPicker();
25622         }
25623     },
25624
25625     hideMonthPicker : function(disableAnim){
25626         if(this.monthPicker){
25627             if(disableAnim === true){
25628                 this.monthPicker.hide();
25629             }else{
25630                 this.monthPicker.slideOut('t', {duration:.2});
25631             }
25632         }
25633     },
25634
25635     // private
25636     showPrevMonth : function(e){
25637         this.update(this.activeDate.add("mo", -1));
25638     },
25639
25640     // private
25641     showNextMonth : function(e){
25642         this.update(this.activeDate.add("mo", 1));
25643     },
25644
25645     // private
25646     showPrevYear : function(){
25647         this.update(this.activeDate.add("y", -1));
25648     },
25649
25650     // private
25651     showNextYear : function(){
25652         this.update(this.activeDate.add("y", 1));
25653     },
25654
25655     // private
25656     handleMouseWheel : function(e){
25657         var delta = e.getWheelDelta();
25658         if(delta > 0){
25659             this.showPrevMonth();
25660             e.stopEvent();
25661         } else if(delta < 0){
25662             this.showNextMonth();
25663             e.stopEvent();
25664         }
25665     },
25666
25667     // private
25668     handleDateClick : function(e, t){
25669         e.stopEvent();
25670         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25671             this.setValue(new Date(t.dateValue));
25672             this.fireEvent("select", this, this.value);
25673         }
25674     },
25675
25676     // private
25677     selectToday : function(){
25678         this.setValue(new Date().clearTime());
25679         this.fireEvent("select", this, this.value);
25680     },
25681
25682     // private
25683     update : function(date)
25684     {
25685         var vd = this.activeDate;
25686         this.activeDate = date;
25687         if(vd && this.el){
25688             var t = date.getTime();
25689             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25690                 this.cells.removeClass("x-date-selected");
25691                 this.cells.each(function(c){
25692                    if(c.dom.firstChild.dateValue == t){
25693                        c.addClass("x-date-selected");
25694                        setTimeout(function(){
25695                             try{c.dom.firstChild.focus();}catch(e){}
25696                        }, 50);
25697                        return false;
25698                    }
25699                 });
25700                 return;
25701             }
25702         }
25703         
25704         var days = date.getDaysInMonth();
25705         var firstOfMonth = date.getFirstDateOfMonth();
25706         var startingPos = firstOfMonth.getDay()-this.startDay;
25707
25708         if(startingPos <= this.startDay){
25709             startingPos += 7;
25710         }
25711
25712         var pm = date.add("mo", -1);
25713         var prevStart = pm.getDaysInMonth()-startingPos;
25714
25715         var cells = this.cells.elements;
25716         var textEls = this.textNodes;
25717         days += startingPos;
25718
25719         // convert everything to numbers so it's fast
25720         var day = 86400000;
25721         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25722         var today = new Date().clearTime().getTime();
25723         var sel = date.clearTime().getTime();
25724         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25725         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25726         var ddMatch = this.disabledDatesRE;
25727         var ddText = this.disabledDatesText;
25728         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25729         var ddaysText = this.disabledDaysText;
25730         var format = this.format;
25731
25732         var setCellClass = function(cal, cell){
25733             cell.title = "";
25734             var t = d.getTime();
25735             cell.firstChild.dateValue = t;
25736             if(t == today){
25737                 cell.className += " x-date-today";
25738                 cell.title = cal.todayText;
25739             }
25740             if(t == sel){
25741                 cell.className += " x-date-selected";
25742                 setTimeout(function(){
25743                     try{cell.firstChild.focus();}catch(e){}
25744                 }, 50);
25745             }
25746             // disabling
25747             if(t < min) {
25748                 cell.className = " x-date-disabled";
25749                 cell.title = cal.minText;
25750                 return;
25751             }
25752             if(t > max) {
25753                 cell.className = " x-date-disabled";
25754                 cell.title = cal.maxText;
25755                 return;
25756             }
25757             if(ddays){
25758                 if(ddays.indexOf(d.getDay()) != -1){
25759                     cell.title = ddaysText;
25760                     cell.className = " x-date-disabled";
25761                 }
25762             }
25763             if(ddMatch && format){
25764                 var fvalue = d.dateFormat(format);
25765                 if(ddMatch.test(fvalue)){
25766                     cell.title = ddText.replace("%0", fvalue);
25767                     cell.className = " x-date-disabled";
25768                 }
25769             }
25770         };
25771
25772         var i = 0;
25773         for(; i < startingPos; i++) {
25774             textEls[i].innerHTML = (++prevStart);
25775             d.setDate(d.getDate()+1);
25776             cells[i].className = "x-date-prevday";
25777             setCellClass(this, cells[i]);
25778         }
25779         for(; i < days; i++){
25780             intDay = i - startingPos + 1;
25781             textEls[i].innerHTML = (intDay);
25782             d.setDate(d.getDate()+1);
25783             cells[i].className = "x-date-active";
25784             setCellClass(this, cells[i]);
25785         }
25786         var extraDays = 0;
25787         for(; i < 42; i++) {
25788              textEls[i].innerHTML = (++extraDays);
25789              d.setDate(d.getDate()+1);
25790              cells[i].className = "x-date-nextday";
25791              setCellClass(this, cells[i]);
25792         }
25793
25794         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25795         this.fireEvent('monthchange', this, date);
25796         
25797         if(!this.internalRender){
25798             var main = this.el.dom.firstChild;
25799             var w = main.offsetWidth;
25800             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25801             Roo.fly(main).setWidth(w);
25802             this.internalRender = true;
25803             // opera does not respect the auto grow header center column
25804             // then, after it gets a width opera refuses to recalculate
25805             // without a second pass
25806             if(Roo.isOpera && !this.secondPass){
25807                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25808                 this.secondPass = true;
25809                 this.update.defer(10, this, [date]);
25810             }
25811         }
25812         
25813         
25814     }
25815 });        /*
25816  * Based on:
25817  * Ext JS Library 1.1.1
25818  * Copyright(c) 2006-2007, Ext JS, LLC.
25819  *
25820  * Originally Released Under LGPL - original licence link has changed is not relivant.
25821  *
25822  * Fork - LGPL
25823  * <script type="text/javascript">
25824  */
25825 /**
25826  * @class Roo.TabPanel
25827  * @extends Roo.util.Observable
25828  * A lightweight tab container.
25829  * <br><br>
25830  * Usage:
25831  * <pre><code>
25832 // basic tabs 1, built from existing content
25833 var tabs = new Roo.TabPanel("tabs1");
25834 tabs.addTab("script", "View Script");
25835 tabs.addTab("markup", "View Markup");
25836 tabs.activate("script");
25837
25838 // more advanced tabs, built from javascript
25839 var jtabs = new Roo.TabPanel("jtabs");
25840 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25841
25842 // set up the UpdateManager
25843 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25844 var updater = tab2.getUpdateManager();
25845 updater.setDefaultUrl("ajax1.htm");
25846 tab2.on('activate', updater.refresh, updater, true);
25847
25848 // Use setUrl for Ajax loading
25849 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25850 tab3.setUrl("ajax2.htm", null, true);
25851
25852 // Disabled tab
25853 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25854 tab4.disable();
25855
25856 jtabs.activate("jtabs-1");
25857  * </code></pre>
25858  * @constructor
25859  * Create a new TabPanel.
25860  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25861  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25862  */
25863 Roo.TabPanel = function(container, config){
25864     /**
25865     * The container element for this TabPanel.
25866     * @type Roo.Element
25867     */
25868     this.el = Roo.get(container, true);
25869     if(config){
25870         if(typeof config == "boolean"){
25871             this.tabPosition = config ? "bottom" : "top";
25872         }else{
25873             Roo.apply(this, config);
25874         }
25875     }
25876     if(this.tabPosition == "bottom"){
25877         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25878         this.el.addClass("x-tabs-bottom");
25879     }
25880     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25881     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25882     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25883     if(Roo.isIE){
25884         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25885     }
25886     if(this.tabPosition != "bottom"){
25887         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25888          * @type Roo.Element
25889          */
25890         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25891         this.el.addClass("x-tabs-top");
25892     }
25893     this.items = [];
25894
25895     this.bodyEl.setStyle("position", "relative");
25896
25897     this.active = null;
25898     this.activateDelegate = this.activate.createDelegate(this);
25899
25900     this.addEvents({
25901         /**
25902          * @event tabchange
25903          * Fires when the active tab changes
25904          * @param {Roo.TabPanel} this
25905          * @param {Roo.TabPanelItem} activePanel The new active tab
25906          */
25907         "tabchange": true,
25908         /**
25909          * @event beforetabchange
25910          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25911          * @param {Roo.TabPanel} this
25912          * @param {Object} e Set cancel to true on this object to cancel the tab change
25913          * @param {Roo.TabPanelItem} tab The tab being changed to
25914          */
25915         "beforetabchange" : true
25916     });
25917
25918     Roo.EventManager.onWindowResize(this.onResize, this);
25919     this.cpad = this.el.getPadding("lr");
25920     this.hiddenCount = 0;
25921
25922
25923     // toolbar on the tabbar support...
25924     if (this.toolbar) {
25925         var tcfg = this.toolbar;
25926         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25927         this.toolbar = new Roo.Toolbar(tcfg);
25928         if (Roo.isSafari) {
25929             var tbl = tcfg.container.child('table', true);
25930             tbl.setAttribute('width', '100%');
25931         }
25932         
25933     }
25934    
25935
25936
25937     Roo.TabPanel.superclass.constructor.call(this);
25938 };
25939
25940 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25941     /*
25942      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25943      */
25944     tabPosition : "top",
25945     /*
25946      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25947      */
25948     currentTabWidth : 0,
25949     /*
25950      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25951      */
25952     minTabWidth : 40,
25953     /*
25954      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25955      */
25956     maxTabWidth : 250,
25957     /*
25958      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25959      */
25960     preferredTabWidth : 175,
25961     /*
25962      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25963      */
25964     resizeTabs : false,
25965     /*
25966      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25967      */
25968     monitorResize : true,
25969     /*
25970      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25971      */
25972     toolbar : false,
25973
25974     /**
25975      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25976      * @param {String} id The id of the div to use <b>or create</b>
25977      * @param {String} text The text for the tab
25978      * @param {String} content (optional) Content to put in the TabPanelItem body
25979      * @param {Boolean} closable (optional) True to create a close icon on the tab
25980      * @return {Roo.TabPanelItem} The created TabPanelItem
25981      */
25982     addTab : function(id, text, content, closable){
25983         var item = new Roo.TabPanelItem(this, id, text, closable);
25984         this.addTabItem(item);
25985         if(content){
25986             item.setContent(content);
25987         }
25988         return item;
25989     },
25990
25991     /**
25992      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25993      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25994      * @return {Roo.TabPanelItem}
25995      */
25996     getTab : function(id){
25997         return this.items[id];
25998     },
25999
26000     /**
26001      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26002      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26003      */
26004     hideTab : function(id){
26005         var t = this.items[id];
26006         if(!t.isHidden()){
26007            t.setHidden(true);
26008            this.hiddenCount++;
26009            this.autoSizeTabs();
26010         }
26011     },
26012
26013     /**
26014      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26015      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26016      */
26017     unhideTab : function(id){
26018         var t = this.items[id];
26019         if(t.isHidden()){
26020            t.setHidden(false);
26021            this.hiddenCount--;
26022            this.autoSizeTabs();
26023         }
26024     },
26025
26026     /**
26027      * Adds an existing {@link Roo.TabPanelItem}.
26028      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26029      */
26030     addTabItem : function(item){
26031         this.items[item.id] = item;
26032         this.items.push(item);
26033         if(this.resizeTabs){
26034            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26035            this.autoSizeTabs();
26036         }else{
26037             item.autoSize();
26038         }
26039     },
26040
26041     /**
26042      * Removes a {@link Roo.TabPanelItem}.
26043      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26044      */
26045     removeTab : function(id){
26046         var items = this.items;
26047         var tab = items[id];
26048         if(!tab) { return; }
26049         var index = items.indexOf(tab);
26050         if(this.active == tab && items.length > 1){
26051             var newTab = this.getNextAvailable(index);
26052             if(newTab) {
26053                 newTab.activate();
26054             }
26055         }
26056         this.stripEl.dom.removeChild(tab.pnode.dom);
26057         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26058             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26059         }
26060         items.splice(index, 1);
26061         delete this.items[tab.id];
26062         tab.fireEvent("close", tab);
26063         tab.purgeListeners();
26064         this.autoSizeTabs();
26065     },
26066
26067     getNextAvailable : function(start){
26068         var items = this.items;
26069         var index = start;
26070         // look for a next tab that will slide over to
26071         // replace the one being removed
26072         while(index < items.length){
26073             var item = items[++index];
26074             if(item && !item.isHidden()){
26075                 return item;
26076             }
26077         }
26078         // if one isn't found select the previous tab (on the left)
26079         index = start;
26080         while(index >= 0){
26081             var item = items[--index];
26082             if(item && !item.isHidden()){
26083                 return item;
26084             }
26085         }
26086         return null;
26087     },
26088
26089     /**
26090      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26091      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26092      */
26093     disableTab : function(id){
26094         var tab = this.items[id];
26095         if(tab && this.active != tab){
26096             tab.disable();
26097         }
26098     },
26099
26100     /**
26101      * Enables a {@link Roo.TabPanelItem} that is disabled.
26102      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26103      */
26104     enableTab : function(id){
26105         var tab = this.items[id];
26106         tab.enable();
26107     },
26108
26109     /**
26110      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26111      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26112      * @return {Roo.TabPanelItem} The TabPanelItem.
26113      */
26114     activate : function(id){
26115         var tab = this.items[id];
26116         if(!tab){
26117             return null;
26118         }
26119         if(tab == this.active || tab.disabled){
26120             return tab;
26121         }
26122         var e = {};
26123         this.fireEvent("beforetabchange", this, e, tab);
26124         if(e.cancel !== true && !tab.disabled){
26125             if(this.active){
26126                 this.active.hide();
26127             }
26128             this.active = this.items[id];
26129             this.active.show();
26130             this.fireEvent("tabchange", this, this.active);
26131         }
26132         return tab;
26133     },
26134
26135     /**
26136      * Gets the active {@link Roo.TabPanelItem}.
26137      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26138      */
26139     getActiveTab : function(){
26140         return this.active;
26141     },
26142
26143     /**
26144      * Updates the tab body element to fit the height of the container element
26145      * for overflow scrolling
26146      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26147      */
26148     syncHeight : function(targetHeight){
26149         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26150         var bm = this.bodyEl.getMargins();
26151         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26152         this.bodyEl.setHeight(newHeight);
26153         return newHeight;
26154     },
26155
26156     onResize : function(){
26157         if(this.monitorResize){
26158             this.autoSizeTabs();
26159         }
26160     },
26161
26162     /**
26163      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26164      */
26165     beginUpdate : function(){
26166         this.updating = true;
26167     },
26168
26169     /**
26170      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26171      */
26172     endUpdate : function(){
26173         this.updating = false;
26174         this.autoSizeTabs();
26175     },
26176
26177     /**
26178      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26179      */
26180     autoSizeTabs : function(){
26181         var count = this.items.length;
26182         var vcount = count - this.hiddenCount;
26183         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26184         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26185         var availWidth = Math.floor(w / vcount);
26186         var b = this.stripBody;
26187         if(b.getWidth() > w){
26188             var tabs = this.items;
26189             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26190             if(availWidth < this.minTabWidth){
26191                 /*if(!this.sleft){    // incomplete scrolling code
26192                     this.createScrollButtons();
26193                 }
26194                 this.showScroll();
26195                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26196             }
26197         }else{
26198             if(this.currentTabWidth < this.preferredTabWidth){
26199                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26200             }
26201         }
26202     },
26203
26204     /**
26205      * Returns the number of tabs in this TabPanel.
26206      * @return {Number}
26207      */
26208      getCount : function(){
26209          return this.items.length;
26210      },
26211
26212     /**
26213      * Resizes all the tabs to the passed width
26214      * @param {Number} The new width
26215      */
26216     setTabWidth : function(width){
26217         this.currentTabWidth = width;
26218         for(var i = 0, len = this.items.length; i < len; i++) {
26219                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26220         }
26221     },
26222
26223     /**
26224      * Destroys this TabPanel
26225      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26226      */
26227     destroy : function(removeEl){
26228         Roo.EventManager.removeResizeListener(this.onResize, this);
26229         for(var i = 0, len = this.items.length; i < len; i++){
26230             this.items[i].purgeListeners();
26231         }
26232         if(removeEl === true){
26233             this.el.update("");
26234             this.el.remove();
26235         }
26236     }
26237 });
26238
26239 /**
26240  * @class Roo.TabPanelItem
26241  * @extends Roo.util.Observable
26242  * Represents an individual item (tab plus body) in a TabPanel.
26243  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26244  * @param {String} id The id of this TabPanelItem
26245  * @param {String} text The text for the tab of this TabPanelItem
26246  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26247  */
26248 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26249     /**
26250      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26251      * @type Roo.TabPanel
26252      */
26253     this.tabPanel = tabPanel;
26254     /**
26255      * The id for this TabPanelItem
26256      * @type String
26257      */
26258     this.id = id;
26259     /** @private */
26260     this.disabled = false;
26261     /** @private */
26262     this.text = text;
26263     /** @private */
26264     this.loaded = false;
26265     this.closable = closable;
26266
26267     /**
26268      * The body element for this TabPanelItem.
26269      * @type Roo.Element
26270      */
26271     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26272     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26273     this.bodyEl.setStyle("display", "block");
26274     this.bodyEl.setStyle("zoom", "1");
26275     this.hideAction();
26276
26277     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26278     /** @private */
26279     this.el = Roo.get(els.el, true);
26280     this.inner = Roo.get(els.inner, true);
26281     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26282     this.pnode = Roo.get(els.el.parentNode, true);
26283     this.el.on("mousedown", this.onTabMouseDown, this);
26284     this.el.on("click", this.onTabClick, this);
26285     /** @private */
26286     if(closable){
26287         var c = Roo.get(els.close, true);
26288         c.dom.title = this.closeText;
26289         c.addClassOnOver("close-over");
26290         c.on("click", this.closeClick, this);
26291      }
26292
26293     this.addEvents({
26294          /**
26295          * @event activate
26296          * Fires when this tab becomes the active tab.
26297          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26298          * @param {Roo.TabPanelItem} this
26299          */
26300         "activate": true,
26301         /**
26302          * @event beforeclose
26303          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26304          * @param {Roo.TabPanelItem} this
26305          * @param {Object} e Set cancel to true on this object to cancel the close.
26306          */
26307         "beforeclose": true,
26308         /**
26309          * @event close
26310          * Fires when this tab is closed.
26311          * @param {Roo.TabPanelItem} this
26312          */
26313          "close": true,
26314         /**
26315          * @event deactivate
26316          * Fires when this tab is no longer the active tab.
26317          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26318          * @param {Roo.TabPanelItem} this
26319          */
26320          "deactivate" : true
26321     });
26322     this.hidden = false;
26323
26324     Roo.TabPanelItem.superclass.constructor.call(this);
26325 };
26326
26327 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26328     purgeListeners : function(){
26329        Roo.util.Observable.prototype.purgeListeners.call(this);
26330        this.el.removeAllListeners();
26331     },
26332     /**
26333      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26334      */
26335     show : function(){
26336         this.pnode.addClass("on");
26337         this.showAction();
26338         if(Roo.isOpera){
26339             this.tabPanel.stripWrap.repaint();
26340         }
26341         this.fireEvent("activate", this.tabPanel, this);
26342     },
26343
26344     /**
26345      * Returns true if this tab is the active tab.
26346      * @return {Boolean}
26347      */
26348     isActive : function(){
26349         return this.tabPanel.getActiveTab() == this;
26350     },
26351
26352     /**
26353      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
26354      */
26355     hide : function(){
26356         this.pnode.removeClass("on");
26357         this.hideAction();
26358         this.fireEvent("deactivate", this.tabPanel, this);
26359     },
26360
26361     hideAction : function(){
26362         this.bodyEl.hide();
26363         this.bodyEl.setStyle("position", "absolute");
26364         this.bodyEl.setLeft("-20000px");
26365         this.bodyEl.setTop("-20000px");
26366     },
26367
26368     showAction : function(){
26369         this.bodyEl.setStyle("position", "relative");
26370         this.bodyEl.setTop("");
26371         this.bodyEl.setLeft("");
26372         this.bodyEl.show();
26373     },
26374
26375     /**
26376      * Set the tooltip for the tab.
26377      * @param {String} tooltip The tab's tooltip
26378      */
26379     setTooltip : function(text){
26380         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
26381             this.textEl.dom.qtip = text;
26382             this.textEl.dom.removeAttribute('title');
26383         }else{
26384             this.textEl.dom.title = text;
26385         }
26386     },
26387
26388     onTabClick : function(e){
26389         e.preventDefault();
26390         this.tabPanel.activate(this.id);
26391     },
26392
26393     onTabMouseDown : function(e){
26394         e.preventDefault();
26395         this.tabPanel.activate(this.id);
26396     },
26397
26398     getWidth : function(){
26399         return this.inner.getWidth();
26400     },
26401
26402     setWidth : function(width){
26403         var iwidth = width - this.pnode.getPadding("lr");
26404         this.inner.setWidth(iwidth);
26405         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
26406         this.pnode.setWidth(width);
26407     },
26408
26409     /**
26410      * Show or hide the tab
26411      * @param {Boolean} hidden True to hide or false to show.
26412      */
26413     setHidden : function(hidden){
26414         this.hidden = hidden;
26415         this.pnode.setStyle("display", hidden ? "none" : "");
26416     },
26417
26418     /**
26419      * Returns true if this tab is "hidden"
26420      * @return {Boolean}
26421      */
26422     isHidden : function(){
26423         return this.hidden;
26424     },
26425
26426     /**
26427      * Returns the text for this tab
26428      * @return {String}
26429      */
26430     getText : function(){
26431         return this.text;
26432     },
26433
26434     autoSize : function(){
26435         //this.el.beginMeasure();
26436         this.textEl.setWidth(1);
26437         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
26438         //this.el.endMeasure();
26439     },
26440
26441     /**
26442      * Sets the text for the tab (Note: this also sets the tooltip text)
26443      * @param {String} text The tab's text and tooltip
26444      */
26445     setText : function(text){
26446         this.text = text;
26447         this.textEl.update(text);
26448         this.setTooltip(text);
26449         if(!this.tabPanel.resizeTabs){
26450             this.autoSize();
26451         }
26452     },
26453     /**
26454      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
26455      */
26456     activate : function(){
26457         this.tabPanel.activate(this.id);
26458     },
26459
26460     /**
26461      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
26462      */
26463     disable : function(){
26464         if(this.tabPanel.active != this){
26465             this.disabled = true;
26466             this.pnode.addClass("disabled");
26467         }
26468     },
26469
26470     /**
26471      * Enables this TabPanelItem if it was previously disabled.
26472      */
26473     enable : function(){
26474         this.disabled = false;
26475         this.pnode.removeClass("disabled");
26476     },
26477
26478     /**
26479      * Sets the content for this TabPanelItem.
26480      * @param {String} content The content
26481      * @param {Boolean} loadScripts true to look for and load scripts
26482      */
26483     setContent : function(content, loadScripts){
26484         this.bodyEl.update(content, loadScripts);
26485     },
26486
26487     /**
26488      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
26489      * @return {Roo.UpdateManager} The UpdateManager
26490      */
26491     getUpdateManager : function(){
26492         return this.bodyEl.getUpdateManager();
26493     },
26494
26495     /**
26496      * Set a URL to be used to load the content for this TabPanelItem.
26497      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
26498      * @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)
26499      * @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)
26500      * @return {Roo.UpdateManager} The UpdateManager
26501      */
26502     setUrl : function(url, params, loadOnce){
26503         if(this.refreshDelegate){
26504             this.un('activate', this.refreshDelegate);
26505         }
26506         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
26507         this.on("activate", this.refreshDelegate);
26508         return this.bodyEl.getUpdateManager();
26509     },
26510
26511     /** @private */
26512     _handleRefresh : function(url, params, loadOnce){
26513         if(!loadOnce || !this.loaded){
26514             var updater = this.bodyEl.getUpdateManager();
26515             updater.update(url, params, this._setLoaded.createDelegate(this));
26516         }
26517     },
26518
26519     /**
26520      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
26521      *   Will fail silently if the setUrl method has not been called.
26522      *   This does not activate the panel, just updates its content.
26523      */
26524     refresh : function(){
26525         if(this.refreshDelegate){
26526            this.loaded = false;
26527            this.refreshDelegate();
26528         }
26529     },
26530
26531     /** @private */
26532     _setLoaded : function(){
26533         this.loaded = true;
26534     },
26535
26536     /** @private */
26537     closeClick : function(e){
26538         var o = {};
26539         e.stopEvent();
26540         this.fireEvent("beforeclose", this, o);
26541         if(o.cancel !== true){
26542             this.tabPanel.removeTab(this.id);
26543         }
26544     },
26545     /**
26546      * The text displayed in the tooltip for the close icon.
26547      * @type String
26548      */
26549     closeText : "Close this tab"
26550 });
26551
26552 /** @private */
26553 Roo.TabPanel.prototype.createStrip = function(container){
26554     var strip = document.createElement("div");
26555     strip.className = "x-tabs-wrap";
26556     container.appendChild(strip);
26557     return strip;
26558 };
26559 /** @private */
26560 Roo.TabPanel.prototype.createStripList = function(strip){
26561     // div wrapper for retard IE
26562     // returns the "tr" element.
26563     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26564         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26565         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26566     return strip.firstChild.firstChild.firstChild.firstChild;
26567 };
26568 /** @private */
26569 Roo.TabPanel.prototype.createBody = function(container){
26570     var body = document.createElement("div");
26571     Roo.id(body, "tab-body");
26572     Roo.fly(body).addClass("x-tabs-body");
26573     container.appendChild(body);
26574     return body;
26575 };
26576 /** @private */
26577 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26578     var body = Roo.getDom(id);
26579     if(!body){
26580         body = document.createElement("div");
26581         body.id = id;
26582     }
26583     Roo.fly(body).addClass("x-tabs-item-body");
26584     bodyEl.insertBefore(body, bodyEl.firstChild);
26585     return body;
26586 };
26587 /** @private */
26588 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26589     var td = document.createElement("td");
26590     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26591     //stripEl.appendChild(td);
26592     if(closable){
26593         td.className = "x-tabs-closable";
26594         if(!this.closeTpl){
26595             this.closeTpl = new Roo.Template(
26596                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26597                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26598                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26599             );
26600         }
26601         var el = this.closeTpl.overwrite(td, {"text": text});
26602         var close = el.getElementsByTagName("div")[0];
26603         var inner = el.getElementsByTagName("em")[0];
26604         return {"el": el, "close": close, "inner": inner};
26605     } else {
26606         if(!this.tabTpl){
26607             this.tabTpl = new Roo.Template(
26608                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26609                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26610             );
26611         }
26612         var el = this.tabTpl.overwrite(td, {"text": text});
26613         var inner = el.getElementsByTagName("em")[0];
26614         return {"el": el, "inner": inner};
26615     }
26616 };/*
26617  * Based on:
26618  * Ext JS Library 1.1.1
26619  * Copyright(c) 2006-2007, Ext JS, LLC.
26620  *
26621  * Originally Released Under LGPL - original licence link has changed is not relivant.
26622  *
26623  * Fork - LGPL
26624  * <script type="text/javascript">
26625  */
26626
26627 /**
26628  * @class Roo.Button
26629  * @extends Roo.util.Observable
26630  * Simple Button class
26631  * @cfg {String} text The button text
26632  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26633  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26634  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26635  * @cfg {Object} scope The scope of the handler
26636  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26637  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26638  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26639  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26640  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26641  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26642    applies if enableToggle = true)
26643  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26644  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26645   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26646  * @constructor
26647  * Create a new button
26648  * @param {Object} config The config object
26649  */
26650 Roo.Button = function(renderTo, config)
26651 {
26652     if (!config) {
26653         config = renderTo;
26654         renderTo = config.renderTo || false;
26655     }
26656     
26657     Roo.apply(this, config);
26658     this.addEvents({
26659         /**
26660              * @event click
26661              * Fires when this button is clicked
26662              * @param {Button} this
26663              * @param {EventObject} e The click event
26664              */
26665             "click" : true,
26666         /**
26667              * @event toggle
26668              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26669              * @param {Button} this
26670              * @param {Boolean} pressed
26671              */
26672             "toggle" : true,
26673         /**
26674              * @event mouseover
26675              * Fires when the mouse hovers over the button
26676              * @param {Button} this
26677              * @param {Event} e The event object
26678              */
26679         'mouseover' : true,
26680         /**
26681              * @event mouseout
26682              * Fires when the mouse exits the button
26683              * @param {Button} this
26684              * @param {Event} e The event object
26685              */
26686         'mouseout': true,
26687          /**
26688              * @event render
26689              * Fires when the button is rendered
26690              * @param {Button} this
26691              */
26692         'render': true
26693     });
26694     if(this.menu){
26695         this.menu = Roo.menu.MenuMgr.get(this.menu);
26696     }
26697     // register listeners first!!  - so render can be captured..
26698     Roo.util.Observable.call(this);
26699     if(renderTo){
26700         this.render(renderTo);
26701     }
26702     
26703   
26704 };
26705
26706 Roo.extend(Roo.Button, Roo.util.Observable, {
26707     /**
26708      * 
26709      */
26710     
26711     /**
26712      * Read-only. True if this button is hidden
26713      * @type Boolean
26714      */
26715     hidden : false,
26716     /**
26717      * Read-only. True if this button is disabled
26718      * @type Boolean
26719      */
26720     disabled : false,
26721     /**
26722      * Read-only. True if this button is pressed (only if enableToggle = true)
26723      * @type Boolean
26724      */
26725     pressed : false,
26726
26727     /**
26728      * @cfg {Number} tabIndex 
26729      * The DOM tabIndex for this button (defaults to undefined)
26730      */
26731     tabIndex : undefined,
26732
26733     /**
26734      * @cfg {Boolean} enableToggle
26735      * True to enable pressed/not pressed toggling (defaults to false)
26736      */
26737     enableToggle: false,
26738     /**
26739      * @cfg {Mixed} menu
26740      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26741      */
26742     menu : undefined,
26743     /**
26744      * @cfg {String} menuAlign
26745      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26746      */
26747     menuAlign : "tl-bl?",
26748
26749     /**
26750      * @cfg {String} iconCls
26751      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26752      */
26753     iconCls : undefined,
26754     /**
26755      * @cfg {String} type
26756      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26757      */
26758     type : 'button',
26759
26760     // private
26761     menuClassTarget: 'tr',
26762
26763     /**
26764      * @cfg {String} clickEvent
26765      * The type of event to map to the button's event handler (defaults to 'click')
26766      */
26767     clickEvent : 'click',
26768
26769     /**
26770      * @cfg {Boolean} handleMouseEvents
26771      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26772      */
26773     handleMouseEvents : true,
26774
26775     /**
26776      * @cfg {String} tooltipType
26777      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26778      */
26779     tooltipType : 'qtip',
26780
26781     /**
26782      * @cfg {String} cls
26783      * A CSS class to apply to the button's main element.
26784      */
26785     
26786     /**
26787      * @cfg {Roo.Template} template (Optional)
26788      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26789      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26790      * require code modifications if required elements (e.g. a button) aren't present.
26791      */
26792
26793     // private
26794     render : function(renderTo){
26795         var btn;
26796         if(this.hideParent){
26797             this.parentEl = Roo.get(renderTo);
26798         }
26799         if(!this.dhconfig){
26800             if(!this.template){
26801                 if(!Roo.Button.buttonTemplate){
26802                     // hideous table template
26803                     Roo.Button.buttonTemplate = new Roo.Template(
26804                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26805                         '<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>',
26806                         "</tr></tbody></table>");
26807                 }
26808                 this.template = Roo.Button.buttonTemplate;
26809             }
26810             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26811             var btnEl = btn.child("button:first");
26812             btnEl.on('focus', this.onFocus, this);
26813             btnEl.on('blur', this.onBlur, this);
26814             if(this.cls){
26815                 btn.addClass(this.cls);
26816             }
26817             if(this.icon){
26818                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26819             }
26820             if(this.iconCls){
26821                 btnEl.addClass(this.iconCls);
26822                 if(!this.cls){
26823                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26824                 }
26825             }
26826             if(this.tabIndex !== undefined){
26827                 btnEl.dom.tabIndex = this.tabIndex;
26828             }
26829             if(this.tooltip){
26830                 if(typeof this.tooltip == 'object'){
26831                     Roo.QuickTips.tips(Roo.apply({
26832                           target: btnEl.id
26833                     }, this.tooltip));
26834                 } else {
26835                     btnEl.dom[this.tooltipType] = this.tooltip;
26836                 }
26837             }
26838         }else{
26839             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26840         }
26841         this.el = btn;
26842         if(this.id){
26843             this.el.dom.id = this.el.id = this.id;
26844         }
26845         if(this.menu){
26846             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26847             this.menu.on("show", this.onMenuShow, this);
26848             this.menu.on("hide", this.onMenuHide, this);
26849         }
26850         btn.addClass("x-btn");
26851         if(Roo.isIE && !Roo.isIE7){
26852             this.autoWidth.defer(1, this);
26853         }else{
26854             this.autoWidth();
26855         }
26856         if(this.handleMouseEvents){
26857             btn.on("mouseover", this.onMouseOver, this);
26858             btn.on("mouseout", this.onMouseOut, this);
26859             btn.on("mousedown", this.onMouseDown, this);
26860         }
26861         btn.on(this.clickEvent, this.onClick, this);
26862         //btn.on("mouseup", this.onMouseUp, this);
26863         if(this.hidden){
26864             this.hide();
26865         }
26866         if(this.disabled){
26867             this.disable();
26868         }
26869         Roo.ButtonToggleMgr.register(this);
26870         if(this.pressed){
26871             this.el.addClass("x-btn-pressed");
26872         }
26873         if(this.repeat){
26874             var repeater = new Roo.util.ClickRepeater(btn,
26875                 typeof this.repeat == "object" ? this.repeat : {}
26876             );
26877             repeater.on("click", this.onClick,  this);
26878         }
26879         
26880         this.fireEvent('render', this);
26881         
26882     },
26883     /**
26884      * Returns the button's underlying element
26885      * @return {Roo.Element} The element
26886      */
26887     getEl : function(){
26888         return this.el;  
26889     },
26890     
26891     /**
26892      * Destroys this Button and removes any listeners.
26893      */
26894     destroy : function(){
26895         Roo.ButtonToggleMgr.unregister(this);
26896         this.el.removeAllListeners();
26897         this.purgeListeners();
26898         this.el.remove();
26899     },
26900
26901     // private
26902     autoWidth : function(){
26903         if(this.el){
26904             this.el.setWidth("auto");
26905             if(Roo.isIE7 && Roo.isStrict){
26906                 var ib = this.el.child('button');
26907                 if(ib && ib.getWidth() > 20){
26908                     ib.clip();
26909                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26910                 }
26911             }
26912             if(this.minWidth){
26913                 if(this.hidden){
26914                     this.el.beginMeasure();
26915                 }
26916                 if(this.el.getWidth() < this.minWidth){
26917                     this.el.setWidth(this.minWidth);
26918                 }
26919                 if(this.hidden){
26920                     this.el.endMeasure();
26921                 }
26922             }
26923         }
26924     },
26925
26926     /**
26927      * Assigns this button's click handler
26928      * @param {Function} handler The function to call when the button is clicked
26929      * @param {Object} scope (optional) Scope for the function passed in
26930      */
26931     setHandler : function(handler, scope){
26932         this.handler = handler;
26933         this.scope = scope;  
26934     },
26935     
26936     /**
26937      * Sets this button's text
26938      * @param {String} text The button text
26939      */
26940     setText : function(text){
26941         this.text = text;
26942         if(this.el){
26943             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26944         }
26945         this.autoWidth();
26946     },
26947     
26948     /**
26949      * Gets the text for this button
26950      * @return {String} The button text
26951      */
26952     getText : function(){
26953         return this.text;  
26954     },
26955     
26956     /**
26957      * Show this button
26958      */
26959     show: function(){
26960         this.hidden = false;
26961         if(this.el){
26962             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26963         }
26964     },
26965     
26966     /**
26967      * Hide this button
26968      */
26969     hide: function(){
26970         this.hidden = true;
26971         if(this.el){
26972             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26973         }
26974     },
26975     
26976     /**
26977      * Convenience function for boolean show/hide
26978      * @param {Boolean} visible True to show, false to hide
26979      */
26980     setVisible: function(visible){
26981         if(visible) {
26982             this.show();
26983         }else{
26984             this.hide();
26985         }
26986     },
26987     
26988     /**
26989      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26990      * @param {Boolean} state (optional) Force a particular state
26991      */
26992     toggle : function(state){
26993         state = state === undefined ? !this.pressed : state;
26994         if(state != this.pressed){
26995             if(state){
26996                 this.el.addClass("x-btn-pressed");
26997                 this.pressed = true;
26998                 this.fireEvent("toggle", this, true);
26999             }else{
27000                 this.el.removeClass("x-btn-pressed");
27001                 this.pressed = false;
27002                 this.fireEvent("toggle", this, false);
27003             }
27004             if(this.toggleHandler){
27005                 this.toggleHandler.call(this.scope || this, this, state);
27006             }
27007         }
27008     },
27009     
27010     /**
27011      * Focus the button
27012      */
27013     focus : function(){
27014         this.el.child('button:first').focus();
27015     },
27016     
27017     /**
27018      * Disable this button
27019      */
27020     disable : function(){
27021         if(this.el){
27022             this.el.addClass("x-btn-disabled");
27023         }
27024         this.disabled = true;
27025     },
27026     
27027     /**
27028      * Enable this button
27029      */
27030     enable : function(){
27031         if(this.el){
27032             this.el.removeClass("x-btn-disabled");
27033         }
27034         this.disabled = false;
27035     },
27036
27037     /**
27038      * Convenience function for boolean enable/disable
27039      * @param {Boolean} enabled True to enable, false to disable
27040      */
27041     setDisabled : function(v){
27042         this[v !== true ? "enable" : "disable"]();
27043     },
27044
27045     // private
27046     onClick : function(e){
27047         if(e){
27048             e.preventDefault();
27049         }
27050         if(e.button != 0){
27051             return;
27052         }
27053         if(!this.disabled){
27054             if(this.enableToggle){
27055                 this.toggle();
27056             }
27057             if(this.menu && !this.menu.isVisible()){
27058                 this.menu.show(this.el, this.menuAlign);
27059             }
27060             this.fireEvent("click", this, e);
27061             if(this.handler){
27062                 this.el.removeClass("x-btn-over");
27063                 this.handler.call(this.scope || this, this, e);
27064             }
27065         }
27066     },
27067     // private
27068     onMouseOver : function(e){
27069         if(!this.disabled){
27070             this.el.addClass("x-btn-over");
27071             this.fireEvent('mouseover', this, e);
27072         }
27073     },
27074     // private
27075     onMouseOut : function(e){
27076         if(!e.within(this.el,  true)){
27077             this.el.removeClass("x-btn-over");
27078             this.fireEvent('mouseout', this, e);
27079         }
27080     },
27081     // private
27082     onFocus : function(e){
27083         if(!this.disabled){
27084             this.el.addClass("x-btn-focus");
27085         }
27086     },
27087     // private
27088     onBlur : function(e){
27089         this.el.removeClass("x-btn-focus");
27090     },
27091     // private
27092     onMouseDown : function(e){
27093         if(!this.disabled && e.button == 0){
27094             this.el.addClass("x-btn-click");
27095             Roo.get(document).on('mouseup', this.onMouseUp, this);
27096         }
27097     },
27098     // private
27099     onMouseUp : function(e){
27100         if(e.button == 0){
27101             this.el.removeClass("x-btn-click");
27102             Roo.get(document).un('mouseup', this.onMouseUp, this);
27103         }
27104     },
27105     // private
27106     onMenuShow : function(e){
27107         this.el.addClass("x-btn-menu-active");
27108     },
27109     // private
27110     onMenuHide : function(e){
27111         this.el.removeClass("x-btn-menu-active");
27112     }   
27113 });
27114
27115 // Private utility class used by Button
27116 Roo.ButtonToggleMgr = function(){
27117    var groups = {};
27118    
27119    function toggleGroup(btn, state){
27120        if(state){
27121            var g = groups[btn.toggleGroup];
27122            for(var i = 0, l = g.length; i < l; i++){
27123                if(g[i] != btn){
27124                    g[i].toggle(false);
27125                }
27126            }
27127        }
27128    }
27129    
27130    return {
27131        register : function(btn){
27132            if(!btn.toggleGroup){
27133                return;
27134            }
27135            var g = groups[btn.toggleGroup];
27136            if(!g){
27137                g = groups[btn.toggleGroup] = [];
27138            }
27139            g.push(btn);
27140            btn.on("toggle", toggleGroup);
27141        },
27142        
27143        unregister : function(btn){
27144            if(!btn.toggleGroup){
27145                return;
27146            }
27147            var g = groups[btn.toggleGroup];
27148            if(g){
27149                g.remove(btn);
27150                btn.un("toggle", toggleGroup);
27151            }
27152        }
27153    };
27154 }();/*
27155  * Based on:
27156  * Ext JS Library 1.1.1
27157  * Copyright(c) 2006-2007, Ext JS, LLC.
27158  *
27159  * Originally Released Under LGPL - original licence link has changed is not relivant.
27160  *
27161  * Fork - LGPL
27162  * <script type="text/javascript">
27163  */
27164  
27165 /**
27166  * @class Roo.SplitButton
27167  * @extends Roo.Button
27168  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27169  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27170  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27171  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27172  * @cfg {String} arrowTooltip The title attribute of the arrow
27173  * @constructor
27174  * Create a new menu button
27175  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27176  * @param {Object} config The config object
27177  */
27178 Roo.SplitButton = function(renderTo, config){
27179     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27180     /**
27181      * @event arrowclick
27182      * Fires when this button's arrow is clicked
27183      * @param {SplitButton} this
27184      * @param {EventObject} e The click event
27185      */
27186     this.addEvents({"arrowclick":true});
27187 };
27188
27189 Roo.extend(Roo.SplitButton, Roo.Button, {
27190     render : function(renderTo){
27191         // this is one sweet looking template!
27192         var tpl = new Roo.Template(
27193             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27194             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27195             '<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>',
27196             "</tbody></table></td><td>",
27197             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27198             '<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>',
27199             "</tbody></table></td></tr></table>"
27200         );
27201         var btn = tpl.append(renderTo, [this.text, this.type], true);
27202         var btnEl = btn.child("button");
27203         if(this.cls){
27204             btn.addClass(this.cls);
27205         }
27206         if(this.icon){
27207             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27208         }
27209         if(this.iconCls){
27210             btnEl.addClass(this.iconCls);
27211             if(!this.cls){
27212                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27213             }
27214         }
27215         this.el = btn;
27216         if(this.handleMouseEvents){
27217             btn.on("mouseover", this.onMouseOver, this);
27218             btn.on("mouseout", this.onMouseOut, this);
27219             btn.on("mousedown", this.onMouseDown, this);
27220             btn.on("mouseup", this.onMouseUp, this);
27221         }
27222         btn.on(this.clickEvent, this.onClick, this);
27223         if(this.tooltip){
27224             if(typeof this.tooltip == 'object'){
27225                 Roo.QuickTips.tips(Roo.apply({
27226                       target: btnEl.id
27227                 }, this.tooltip));
27228             } else {
27229                 btnEl.dom[this.tooltipType] = this.tooltip;
27230             }
27231         }
27232         if(this.arrowTooltip){
27233             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27234         }
27235         if(this.hidden){
27236             this.hide();
27237         }
27238         if(this.disabled){
27239             this.disable();
27240         }
27241         if(this.pressed){
27242             this.el.addClass("x-btn-pressed");
27243         }
27244         if(Roo.isIE && !Roo.isIE7){
27245             this.autoWidth.defer(1, this);
27246         }else{
27247             this.autoWidth();
27248         }
27249         if(this.menu){
27250             this.menu.on("show", this.onMenuShow, this);
27251             this.menu.on("hide", this.onMenuHide, this);
27252         }
27253         this.fireEvent('render', this);
27254     },
27255
27256     // private
27257     autoWidth : function(){
27258         if(this.el){
27259             var tbl = this.el.child("table:first");
27260             var tbl2 = this.el.child("table:last");
27261             this.el.setWidth("auto");
27262             tbl.setWidth("auto");
27263             if(Roo.isIE7 && Roo.isStrict){
27264                 var ib = this.el.child('button:first');
27265                 if(ib && ib.getWidth() > 20){
27266                     ib.clip();
27267                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27268                 }
27269             }
27270             if(this.minWidth){
27271                 if(this.hidden){
27272                     this.el.beginMeasure();
27273                 }
27274                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27275                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27276                 }
27277                 if(this.hidden){
27278                     this.el.endMeasure();
27279                 }
27280             }
27281             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27282         } 
27283     },
27284     /**
27285      * Sets this button's click handler
27286      * @param {Function} handler The function to call when the button is clicked
27287      * @param {Object} scope (optional) Scope for the function passed above
27288      */
27289     setHandler : function(handler, scope){
27290         this.handler = handler;
27291         this.scope = scope;  
27292     },
27293     
27294     /**
27295      * Sets this button's arrow click handler
27296      * @param {Function} handler The function to call when the arrow is clicked
27297      * @param {Object} scope (optional) Scope for the function passed above
27298      */
27299     setArrowHandler : function(handler, scope){
27300         this.arrowHandler = handler;
27301         this.scope = scope;  
27302     },
27303     
27304     /**
27305      * Focus the button
27306      */
27307     focus : function(){
27308         if(this.el){
27309             this.el.child("button:first").focus();
27310         }
27311     },
27312
27313     // private
27314     onClick : function(e){
27315         e.preventDefault();
27316         if(!this.disabled){
27317             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27318                 if(this.menu && !this.menu.isVisible()){
27319                     this.menu.show(this.el, this.menuAlign);
27320                 }
27321                 this.fireEvent("arrowclick", this, e);
27322                 if(this.arrowHandler){
27323                     this.arrowHandler.call(this.scope || this, this, e);
27324                 }
27325             }else{
27326                 this.fireEvent("click", this, e);
27327                 if(this.handler){
27328                     this.handler.call(this.scope || this, this, e);
27329                 }
27330             }
27331         }
27332     },
27333     // private
27334     onMouseDown : function(e){
27335         if(!this.disabled){
27336             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27337         }
27338     },
27339     // private
27340     onMouseUp : function(e){
27341         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27342     }   
27343 });
27344
27345
27346 // backwards compat
27347 Roo.MenuButton = Roo.SplitButton;/*
27348  * Based on:
27349  * Ext JS Library 1.1.1
27350  * Copyright(c) 2006-2007, Ext JS, LLC.
27351  *
27352  * Originally Released Under LGPL - original licence link has changed is not relivant.
27353  *
27354  * Fork - LGPL
27355  * <script type="text/javascript">
27356  */
27357
27358 /**
27359  * @class Roo.Toolbar
27360  * Basic Toolbar class.
27361  * @constructor
27362  * Creates a new Toolbar
27363  * @param {Object} container The config object
27364  */ 
27365 Roo.Toolbar = function(container, buttons, config)
27366 {
27367     /// old consturctor format still supported..
27368     if(container instanceof Array){ // omit the container for later rendering
27369         buttons = container;
27370         config = buttons;
27371         container = null;
27372     }
27373     if (typeof(container) == 'object' && container.xtype) {
27374         config = container;
27375         container = config.container;
27376         buttons = config.buttons || []; // not really - use items!!
27377     }
27378     var xitems = [];
27379     if (config && config.items) {
27380         xitems = config.items;
27381         delete config.items;
27382     }
27383     Roo.apply(this, config);
27384     this.buttons = buttons;
27385     
27386     if(container){
27387         this.render(container);
27388     }
27389     this.xitems = xitems;
27390     Roo.each(xitems, function(b) {
27391         this.add(b);
27392     }, this);
27393     
27394 };
27395
27396 Roo.Toolbar.prototype = {
27397     /**
27398      * @cfg {Array} items
27399      * array of button configs or elements to add (will be converted to a MixedCollection)
27400      */
27401     
27402     /**
27403      * @cfg {String/HTMLElement/Element} container
27404      * The id or element that will contain the toolbar
27405      */
27406     // private
27407     render : function(ct){
27408         this.el = Roo.get(ct);
27409         if(this.cls){
27410             this.el.addClass(this.cls);
27411         }
27412         // using a table allows for vertical alignment
27413         // 100% width is needed by Safari...
27414         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
27415         this.tr = this.el.child("tr", true);
27416         var autoId = 0;
27417         this.items = new Roo.util.MixedCollection(false, function(o){
27418             return o.id || ("item" + (++autoId));
27419         });
27420         if(this.buttons){
27421             this.add.apply(this, this.buttons);
27422             delete this.buttons;
27423         }
27424     },
27425
27426     /**
27427      * Adds element(s) to the toolbar -- this function takes a variable number of 
27428      * arguments of mixed type and adds them to the toolbar.
27429      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
27430      * <ul>
27431      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
27432      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
27433      * <li>Field: Any form field (equivalent to {@link #addField})</li>
27434      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
27435      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
27436      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
27437      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
27438      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
27439      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
27440      * </ul>
27441      * @param {Mixed} arg2
27442      * @param {Mixed} etc.
27443      */
27444     add : function(){
27445         var a = arguments, l = a.length;
27446         for(var i = 0; i < l; i++){
27447             this._add(a[i]);
27448         }
27449     },
27450     // private..
27451     _add : function(el) {
27452         
27453         if (el.xtype) {
27454             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
27455         }
27456         
27457         if (el.applyTo){ // some kind of form field
27458             return this.addField(el);
27459         } 
27460         if (el.render){ // some kind of Toolbar.Item
27461             return this.addItem(el);
27462         }
27463         if (typeof el == "string"){ // string
27464             if(el == "separator" || el == "-"){
27465                 return this.addSeparator();
27466             }
27467             if (el == " "){
27468                 return this.addSpacer();
27469             }
27470             if(el == "->"){
27471                 return this.addFill();
27472             }
27473             return this.addText(el);
27474             
27475         }
27476         if(el.tagName){ // element
27477             return this.addElement(el);
27478         }
27479         if(typeof el == "object"){ // must be button config?
27480             return this.addButton(el);
27481         }
27482         // and now what?!?!
27483         return false;
27484         
27485     },
27486     
27487     /**
27488      * Add an Xtype element
27489      * @param {Object} xtype Xtype Object
27490      * @return {Object} created Object
27491      */
27492     addxtype : function(e){
27493         return this.add(e);  
27494     },
27495     
27496     /**
27497      * Returns the Element for this toolbar.
27498      * @return {Roo.Element}
27499      */
27500     getEl : function(){
27501         return this.el;  
27502     },
27503     
27504     /**
27505      * Adds a separator
27506      * @return {Roo.Toolbar.Item} The separator item
27507      */
27508     addSeparator : function(){
27509         return this.addItem(new Roo.Toolbar.Separator());
27510     },
27511
27512     /**
27513      * Adds a spacer element
27514      * @return {Roo.Toolbar.Spacer} The spacer item
27515      */
27516     addSpacer : function(){
27517         return this.addItem(new Roo.Toolbar.Spacer());
27518     },
27519
27520     /**
27521      * Adds a fill element that forces subsequent additions to the right side of the toolbar
27522      * @return {Roo.Toolbar.Fill} The fill item
27523      */
27524     addFill : function(){
27525         return this.addItem(new Roo.Toolbar.Fill());
27526     },
27527
27528     /**
27529      * Adds any standard HTML element to the toolbar
27530      * @param {String/HTMLElement/Element} el The element or id of the element to add
27531      * @return {Roo.Toolbar.Item} The element's item
27532      */
27533     addElement : function(el){
27534         return this.addItem(new Roo.Toolbar.Item(el));
27535     },
27536     /**
27537      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
27538      * @type Roo.util.MixedCollection  
27539      */
27540     items : false,
27541      
27542     /**
27543      * Adds any Toolbar.Item or subclass
27544      * @param {Roo.Toolbar.Item} item
27545      * @return {Roo.Toolbar.Item} The item
27546      */
27547     addItem : function(item){
27548         var td = this.nextBlock();
27549         item.render(td);
27550         this.items.add(item);
27551         return item;
27552     },
27553     
27554     /**
27555      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
27556      * @param {Object/Array} config A button config or array of configs
27557      * @return {Roo.Toolbar.Button/Array}
27558      */
27559     addButton : function(config){
27560         if(config instanceof Array){
27561             var buttons = [];
27562             for(var i = 0, len = config.length; i < len; i++) {
27563                 buttons.push(this.addButton(config[i]));
27564             }
27565             return buttons;
27566         }
27567         var b = config;
27568         if(!(config instanceof Roo.Toolbar.Button)){
27569             b = config.split ?
27570                 new Roo.Toolbar.SplitButton(config) :
27571                 new Roo.Toolbar.Button(config);
27572         }
27573         var td = this.nextBlock();
27574         b.render(td);
27575         this.items.add(b);
27576         return b;
27577     },
27578     
27579     /**
27580      * Adds text to the toolbar
27581      * @param {String} text The text to add
27582      * @return {Roo.Toolbar.Item} The element's item
27583      */
27584     addText : function(text){
27585         return this.addItem(new Roo.Toolbar.TextItem(text));
27586     },
27587     
27588     /**
27589      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27590      * @param {Number} index The index where the item is to be inserted
27591      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27592      * @return {Roo.Toolbar.Button/Item}
27593      */
27594     insertButton : function(index, item){
27595         if(item instanceof Array){
27596             var buttons = [];
27597             for(var i = 0, len = item.length; i < len; i++) {
27598                buttons.push(this.insertButton(index + i, item[i]));
27599             }
27600             return buttons;
27601         }
27602         if (!(item instanceof Roo.Toolbar.Button)){
27603            item = new Roo.Toolbar.Button(item);
27604         }
27605         var td = document.createElement("td");
27606         this.tr.insertBefore(td, this.tr.childNodes[index]);
27607         item.render(td);
27608         this.items.insert(index, item);
27609         return item;
27610     },
27611     
27612     /**
27613      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27614      * @param {Object} config
27615      * @return {Roo.Toolbar.Item} The element's item
27616      */
27617     addDom : function(config, returnEl){
27618         var td = this.nextBlock();
27619         Roo.DomHelper.overwrite(td, config);
27620         var ti = new Roo.Toolbar.Item(td.firstChild);
27621         ti.render(td);
27622         this.items.add(ti);
27623         return ti;
27624     },
27625
27626     /**
27627      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27628      * @type Roo.util.MixedCollection  
27629      */
27630     fields : false,
27631     
27632     /**
27633      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27634      * Note: the field should not have been rendered yet. For a field that has already been
27635      * rendered, use {@link #addElement}.
27636      * @param {Roo.form.Field} field
27637      * @return {Roo.ToolbarItem}
27638      */
27639      
27640       
27641     addField : function(field) {
27642         if (!this.fields) {
27643             var autoId = 0;
27644             this.fields = new Roo.util.MixedCollection(false, function(o){
27645                 return o.id || ("item" + (++autoId));
27646             });
27647
27648         }
27649         
27650         var td = this.nextBlock();
27651         field.render(td);
27652         var ti = new Roo.Toolbar.Item(td.firstChild);
27653         ti.render(td);
27654         this.items.add(ti);
27655         this.fields.add(field);
27656         return ti;
27657     },
27658     /**
27659      * Hide the toolbar
27660      * @method hide
27661      */
27662      
27663       
27664     hide : function()
27665     {
27666         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27667         this.el.child('div').hide();
27668     },
27669     /**
27670      * Show the toolbar
27671      * @method show
27672      */
27673     show : function()
27674     {
27675         this.el.child('div').show();
27676     },
27677       
27678     // private
27679     nextBlock : function(){
27680         var td = document.createElement("td");
27681         this.tr.appendChild(td);
27682         return td;
27683     },
27684
27685     // private
27686     destroy : function(){
27687         if(this.items){ // rendered?
27688             Roo.destroy.apply(Roo, this.items.items);
27689         }
27690         if(this.fields){ // rendered?
27691             Roo.destroy.apply(Roo, this.fields.items);
27692         }
27693         Roo.Element.uncache(this.el, this.tr);
27694     }
27695 };
27696
27697 /**
27698  * @class Roo.Toolbar.Item
27699  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27700  * @constructor
27701  * Creates a new Item
27702  * @param {HTMLElement} el 
27703  */
27704 Roo.Toolbar.Item = function(el){
27705     this.el = Roo.getDom(el);
27706     this.id = Roo.id(this.el);
27707     this.hidden = false;
27708 };
27709
27710 Roo.Toolbar.Item.prototype = {
27711     
27712     /**
27713      * Get this item's HTML Element
27714      * @return {HTMLElement}
27715      */
27716     getEl : function(){
27717        return this.el;  
27718     },
27719
27720     // private
27721     render : function(td){
27722         this.td = td;
27723         td.appendChild(this.el);
27724     },
27725     
27726     /**
27727      * Removes and destroys this item.
27728      */
27729     destroy : function(){
27730         this.td.parentNode.removeChild(this.td);
27731     },
27732     
27733     /**
27734      * Shows this item.
27735      */
27736     show: function(){
27737         this.hidden = false;
27738         this.td.style.display = "";
27739     },
27740     
27741     /**
27742      * Hides this item.
27743      */
27744     hide: function(){
27745         this.hidden = true;
27746         this.td.style.display = "none";
27747     },
27748     
27749     /**
27750      * Convenience function for boolean show/hide.
27751      * @param {Boolean} visible true to show/false to hide
27752      */
27753     setVisible: function(visible){
27754         if(visible) {
27755             this.show();
27756         }else{
27757             this.hide();
27758         }
27759     },
27760     
27761     /**
27762      * Try to focus this item.
27763      */
27764     focus : function(){
27765         Roo.fly(this.el).focus();
27766     },
27767     
27768     /**
27769      * Disables this item.
27770      */
27771     disable : function(){
27772         Roo.fly(this.td).addClass("x-item-disabled");
27773         this.disabled = true;
27774         this.el.disabled = true;
27775     },
27776     
27777     /**
27778      * Enables this item.
27779      */
27780     enable : function(){
27781         Roo.fly(this.td).removeClass("x-item-disabled");
27782         this.disabled = false;
27783         this.el.disabled = false;
27784     }
27785 };
27786
27787
27788 /**
27789  * @class Roo.Toolbar.Separator
27790  * @extends Roo.Toolbar.Item
27791  * A simple toolbar separator class
27792  * @constructor
27793  * Creates a new Separator
27794  */
27795 Roo.Toolbar.Separator = function(){
27796     var s = document.createElement("span");
27797     s.className = "ytb-sep";
27798     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27799 };
27800 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27801     enable:Roo.emptyFn,
27802     disable:Roo.emptyFn,
27803     focus:Roo.emptyFn
27804 });
27805
27806 /**
27807  * @class Roo.Toolbar.Spacer
27808  * @extends Roo.Toolbar.Item
27809  * A simple element that adds extra horizontal space to a toolbar.
27810  * @constructor
27811  * Creates a new Spacer
27812  */
27813 Roo.Toolbar.Spacer = function(){
27814     var s = document.createElement("div");
27815     s.className = "ytb-spacer";
27816     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27817 };
27818 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27819     enable:Roo.emptyFn,
27820     disable:Roo.emptyFn,
27821     focus:Roo.emptyFn
27822 });
27823
27824 /**
27825  * @class Roo.Toolbar.Fill
27826  * @extends Roo.Toolbar.Spacer
27827  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27828  * @constructor
27829  * Creates a new Spacer
27830  */
27831 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27832     // private
27833     render : function(td){
27834         td.style.width = '100%';
27835         Roo.Toolbar.Fill.superclass.render.call(this, td);
27836     }
27837 });
27838
27839 /**
27840  * @class Roo.Toolbar.TextItem
27841  * @extends Roo.Toolbar.Item
27842  * A simple class that renders text directly into a toolbar.
27843  * @constructor
27844  * Creates a new TextItem
27845  * @param {String} text
27846  */
27847 Roo.Toolbar.TextItem = function(text){
27848     if (typeof(text) == 'object') {
27849         text = text.text;
27850     }
27851     var s = document.createElement("span");
27852     s.className = "ytb-text";
27853     s.innerHTML = text;
27854     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27855 };
27856 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27857     enable:Roo.emptyFn,
27858     disable:Roo.emptyFn,
27859     focus:Roo.emptyFn
27860 });
27861
27862 /**
27863  * @class Roo.Toolbar.Button
27864  * @extends Roo.Button
27865  * A button that renders into a toolbar.
27866  * @constructor
27867  * Creates a new Button
27868  * @param {Object} config A standard {@link Roo.Button} config object
27869  */
27870 Roo.Toolbar.Button = function(config){
27871     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27872 };
27873 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27874     render : function(td){
27875         this.td = td;
27876         Roo.Toolbar.Button.superclass.render.call(this, td);
27877     },
27878     
27879     /**
27880      * Removes and destroys this button
27881      */
27882     destroy : function(){
27883         Roo.Toolbar.Button.superclass.destroy.call(this);
27884         this.td.parentNode.removeChild(this.td);
27885     },
27886     
27887     /**
27888      * Shows this button
27889      */
27890     show: function(){
27891         this.hidden = false;
27892         this.td.style.display = "";
27893     },
27894     
27895     /**
27896      * Hides this button
27897      */
27898     hide: function(){
27899         this.hidden = true;
27900         this.td.style.display = "none";
27901     },
27902
27903     /**
27904      * Disables this item
27905      */
27906     disable : function(){
27907         Roo.fly(this.td).addClass("x-item-disabled");
27908         this.disabled = true;
27909     },
27910
27911     /**
27912      * Enables this item
27913      */
27914     enable : function(){
27915         Roo.fly(this.td).removeClass("x-item-disabled");
27916         this.disabled = false;
27917     }
27918 });
27919 // backwards compat
27920 Roo.ToolbarButton = Roo.Toolbar.Button;
27921
27922 /**
27923  * @class Roo.Toolbar.SplitButton
27924  * @extends Roo.SplitButton
27925  * A menu button that renders into a toolbar.
27926  * @constructor
27927  * Creates a new SplitButton
27928  * @param {Object} config A standard {@link Roo.SplitButton} config object
27929  */
27930 Roo.Toolbar.SplitButton = function(config){
27931     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27932 };
27933 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27934     render : function(td){
27935         this.td = td;
27936         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27937     },
27938     
27939     /**
27940      * Removes and destroys this button
27941      */
27942     destroy : function(){
27943         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27944         this.td.parentNode.removeChild(this.td);
27945     },
27946     
27947     /**
27948      * Shows this button
27949      */
27950     show: function(){
27951         this.hidden = false;
27952         this.td.style.display = "";
27953     },
27954     
27955     /**
27956      * Hides this button
27957      */
27958     hide: function(){
27959         this.hidden = true;
27960         this.td.style.display = "none";
27961     }
27962 });
27963
27964 // backwards compat
27965 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27966  * Based on:
27967  * Ext JS Library 1.1.1
27968  * Copyright(c) 2006-2007, Ext JS, LLC.
27969  *
27970  * Originally Released Under LGPL - original licence link has changed is not relivant.
27971  *
27972  * Fork - LGPL
27973  * <script type="text/javascript">
27974  */
27975  
27976 /**
27977  * @class Roo.PagingToolbar
27978  * @extends Roo.Toolbar
27979  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27980  * @constructor
27981  * Create a new PagingToolbar
27982  * @param {Object} config The config object
27983  */
27984 Roo.PagingToolbar = function(el, ds, config)
27985 {
27986     // old args format still supported... - xtype is prefered..
27987     if (typeof(el) == 'object' && el.xtype) {
27988         // created from xtype...
27989         config = el;
27990         ds = el.dataSource;
27991         el = config.container;
27992     }
27993     var items = [];
27994     if (config.items) {
27995         items = config.items;
27996         config.items = [];
27997     }
27998     
27999     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28000     this.ds = ds;
28001     this.cursor = 0;
28002     this.renderButtons(this.el);
28003     this.bind(ds);
28004     
28005     // supprot items array.
28006    
28007     Roo.each(items, function(e) {
28008         this.add(Roo.factory(e));
28009     },this);
28010     
28011 };
28012
28013 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28014     /**
28015      * @cfg {Roo.data.Store} dataSource
28016      * The underlying data store providing the paged data
28017      */
28018     /**
28019      * @cfg {String/HTMLElement/Element} container
28020      * container The id or element that will contain the toolbar
28021      */
28022     /**
28023      * @cfg {Boolean} displayInfo
28024      * True to display the displayMsg (defaults to false)
28025      */
28026     /**
28027      * @cfg {Number} pageSize
28028      * The number of records to display per page (defaults to 20)
28029      */
28030     pageSize: 20,
28031     /**
28032      * @cfg {String} displayMsg
28033      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28034      */
28035     displayMsg : 'Displaying {0} - {1} of {2}',
28036     /**
28037      * @cfg {String} emptyMsg
28038      * The message to display when no records are found (defaults to "No data to display")
28039      */
28040     emptyMsg : 'No data to display',
28041     /**
28042      * Customizable piece of the default paging text (defaults to "Page")
28043      * @type String
28044      */
28045     beforePageText : "Page",
28046     /**
28047      * Customizable piece of the default paging text (defaults to "of %0")
28048      * @type String
28049      */
28050     afterPageText : "of {0}",
28051     /**
28052      * Customizable piece of the default paging text (defaults to "First Page")
28053      * @type String
28054      */
28055     firstText : "First Page",
28056     /**
28057      * Customizable piece of the default paging text (defaults to "Previous Page")
28058      * @type String
28059      */
28060     prevText : "Previous Page",
28061     /**
28062      * Customizable piece of the default paging text (defaults to "Next Page")
28063      * @type String
28064      */
28065     nextText : "Next Page",
28066     /**
28067      * Customizable piece of the default paging text (defaults to "Last Page")
28068      * @type String
28069      */
28070     lastText : "Last Page",
28071     /**
28072      * Customizable piece of the default paging text (defaults to "Refresh")
28073      * @type String
28074      */
28075     refreshText : "Refresh",
28076
28077     // private
28078     renderButtons : function(el){
28079         Roo.PagingToolbar.superclass.render.call(this, el);
28080         this.first = this.addButton({
28081             tooltip: this.firstText,
28082             cls: "x-btn-icon x-grid-page-first",
28083             disabled: true,
28084             handler: this.onClick.createDelegate(this, ["first"])
28085         });
28086         this.prev = this.addButton({
28087             tooltip: this.prevText,
28088             cls: "x-btn-icon x-grid-page-prev",
28089             disabled: true,
28090             handler: this.onClick.createDelegate(this, ["prev"])
28091         });
28092         //this.addSeparator();
28093         this.add(this.beforePageText);
28094         this.field = Roo.get(this.addDom({
28095            tag: "input",
28096            type: "text",
28097            size: "3",
28098            value: "1",
28099            cls: "x-grid-page-number"
28100         }).el);
28101         this.field.on("keydown", this.onPagingKeydown, this);
28102         this.field.on("focus", function(){this.dom.select();});
28103         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28104         this.field.setHeight(18);
28105         //this.addSeparator();
28106         this.next = this.addButton({
28107             tooltip: this.nextText,
28108             cls: "x-btn-icon x-grid-page-next",
28109             disabled: true,
28110             handler: this.onClick.createDelegate(this, ["next"])
28111         });
28112         this.last = this.addButton({
28113             tooltip: this.lastText,
28114             cls: "x-btn-icon x-grid-page-last",
28115             disabled: true,
28116             handler: this.onClick.createDelegate(this, ["last"])
28117         });
28118         //this.addSeparator();
28119         this.loading = this.addButton({
28120             tooltip: this.refreshText,
28121             cls: "x-btn-icon x-grid-loading",
28122             handler: this.onClick.createDelegate(this, ["refresh"])
28123         });
28124
28125         if(this.displayInfo){
28126             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28127         }
28128     },
28129
28130     // private
28131     updateInfo : function(){
28132         if(this.displayEl){
28133             var count = this.ds.getCount();
28134             var msg = count == 0 ?
28135                 this.emptyMsg :
28136                 String.format(
28137                     this.displayMsg,
28138                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28139                 );
28140             this.displayEl.update(msg);
28141         }
28142     },
28143
28144     // private
28145     onLoad : function(ds, r, o){
28146        this.cursor = o.params ? o.params.start : 0;
28147        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28148
28149        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28150        this.field.dom.value = ap;
28151        this.first.setDisabled(ap == 1);
28152        this.prev.setDisabled(ap == 1);
28153        this.next.setDisabled(ap == ps);
28154        this.last.setDisabled(ap == ps);
28155        this.loading.enable();
28156        this.updateInfo();
28157     },
28158
28159     // private
28160     getPageData : function(){
28161         var total = this.ds.getTotalCount();
28162         return {
28163             total : total,
28164             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28165             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28166         };
28167     },
28168
28169     // private
28170     onLoadError : function(){
28171         this.loading.enable();
28172     },
28173
28174     // private
28175     onPagingKeydown : function(e){
28176         var k = e.getKey();
28177         var d = this.getPageData();
28178         if(k == e.RETURN){
28179             var v = this.field.dom.value, pageNum;
28180             if(!v || isNaN(pageNum = parseInt(v, 10))){
28181                 this.field.dom.value = d.activePage;
28182                 return;
28183             }
28184             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28185             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28186             e.stopEvent();
28187         }
28188         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))
28189         {
28190           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28191           this.field.dom.value = pageNum;
28192           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28193           e.stopEvent();
28194         }
28195         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28196         {
28197           var v = this.field.dom.value, pageNum; 
28198           var increment = (e.shiftKey) ? 10 : 1;
28199           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28200             increment *= -1;
28201           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28202             this.field.dom.value = d.activePage;
28203             return;
28204           }
28205           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28206           {
28207             this.field.dom.value = parseInt(v, 10) + increment;
28208             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28209             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28210           }
28211           e.stopEvent();
28212         }
28213     },
28214
28215     // private
28216     beforeLoad : function(){
28217         if(this.loading){
28218             this.loading.disable();
28219         }
28220     },
28221
28222     // private
28223     onClick : function(which){
28224         var ds = this.ds;
28225         switch(which){
28226             case "first":
28227                 ds.load({params:{start: 0, limit: this.pageSize}});
28228             break;
28229             case "prev":
28230                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28231             break;
28232             case "next":
28233                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28234             break;
28235             case "last":
28236                 var total = ds.getTotalCount();
28237                 var extra = total % this.pageSize;
28238                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28239                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28240             break;
28241             case "refresh":
28242                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28243             break;
28244         }
28245     },
28246
28247     /**
28248      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28249      * @param {Roo.data.Store} store The data store to unbind
28250      */
28251     unbind : function(ds){
28252         ds.un("beforeload", this.beforeLoad, this);
28253         ds.un("load", this.onLoad, this);
28254         ds.un("loadexception", this.onLoadError, this);
28255         ds.un("remove", this.updateInfo, this);
28256         ds.un("add", this.updateInfo, this);
28257         this.ds = undefined;
28258     },
28259
28260     /**
28261      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28262      * @param {Roo.data.Store} store The data store to bind
28263      */
28264     bind : function(ds){
28265         ds.on("beforeload", this.beforeLoad, this);
28266         ds.on("load", this.onLoad, this);
28267         ds.on("loadexception", this.onLoadError, this);
28268         ds.on("remove", this.updateInfo, this);
28269         ds.on("add", this.updateInfo, this);
28270         this.ds = ds;
28271     }
28272 });/*
28273  * Based on:
28274  * Ext JS Library 1.1.1
28275  * Copyright(c) 2006-2007, Ext JS, LLC.
28276  *
28277  * Originally Released Under LGPL - original licence link has changed is not relivant.
28278  *
28279  * Fork - LGPL
28280  * <script type="text/javascript">
28281  */
28282
28283 /**
28284  * @class Roo.Resizable
28285  * @extends Roo.util.Observable
28286  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28287  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28288  * 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
28289  * the element will be wrapped for you automatically.</p>
28290  * <p>Here is the list of valid resize handles:</p>
28291  * <pre>
28292 Value   Description
28293 ------  -------------------
28294  'n'     north
28295  's'     south
28296  'e'     east
28297  'w'     west
28298  'nw'    northwest
28299  'sw'    southwest
28300  'se'    southeast
28301  'ne'    northeast
28302  'hd'    horizontal drag
28303  'all'   all
28304 </pre>
28305  * <p>Here's an example showing the creation of a typical Resizable:</p>
28306  * <pre><code>
28307 var resizer = new Roo.Resizable("element-id", {
28308     handles: 'all',
28309     minWidth: 200,
28310     minHeight: 100,
28311     maxWidth: 500,
28312     maxHeight: 400,
28313     pinned: true
28314 });
28315 resizer.on("resize", myHandler);
28316 </code></pre>
28317  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28318  * resizer.east.setDisplayed(false);</p>
28319  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28320  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28321  * resize operation's new size (defaults to [0, 0])
28322  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28323  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28324  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28325  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28326  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28327  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28328  * @cfg {Number} width The width of the element in pixels (defaults to null)
28329  * @cfg {Number} height The height of the element in pixels (defaults to null)
28330  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28331  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28332  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28333  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28334  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28335  * in favor of the handles config option (defaults to false)
28336  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28337  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28338  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28339  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28340  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28341  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28342  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28343  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28344  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28345  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28346  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28347  * @constructor
28348  * Create a new resizable component
28349  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
28350  * @param {Object} config configuration options
28351   */
28352 Roo.Resizable = function(el, config)
28353 {
28354     this.el = Roo.get(el);
28355
28356     if(config && config.wrap){
28357         config.resizeChild = this.el;
28358         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
28359         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
28360         this.el.setStyle("overflow", "hidden");
28361         this.el.setPositioning(config.resizeChild.getPositioning());
28362         config.resizeChild.clearPositioning();
28363         if(!config.width || !config.height){
28364             var csize = config.resizeChild.getSize();
28365             this.el.setSize(csize.width, csize.height);
28366         }
28367         if(config.pinned && !config.adjustments){
28368             config.adjustments = "auto";
28369         }
28370     }
28371
28372     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
28373     this.proxy.unselectable();
28374     this.proxy.enableDisplayMode('block');
28375
28376     Roo.apply(this, config);
28377
28378     if(this.pinned){
28379         this.disableTrackOver = true;
28380         this.el.addClass("x-resizable-pinned");
28381     }
28382     // if the element isn't positioned, make it relative
28383     var position = this.el.getStyle("position");
28384     if(position != "absolute" && position != "fixed"){
28385         this.el.setStyle("position", "relative");
28386     }
28387     if(!this.handles){ // no handles passed, must be legacy style
28388         this.handles = 's,e,se';
28389         if(this.multiDirectional){
28390             this.handles += ',n,w';
28391         }
28392     }
28393     if(this.handles == "all"){
28394         this.handles = "n s e w ne nw se sw";
28395     }
28396     var hs = this.handles.split(/\s*?[,;]\s*?| /);
28397     var ps = Roo.Resizable.positions;
28398     for(var i = 0, len = hs.length; i < len; i++){
28399         if(hs[i] && ps[hs[i]]){
28400             var pos = ps[hs[i]];
28401             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
28402         }
28403     }
28404     // legacy
28405     this.corner = this.southeast;
28406     
28407     // updateBox = the box can move..
28408     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
28409         this.updateBox = true;
28410     }
28411
28412     this.activeHandle = null;
28413
28414     if(this.resizeChild){
28415         if(typeof this.resizeChild == "boolean"){
28416             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
28417         }else{
28418             this.resizeChild = Roo.get(this.resizeChild, true);
28419         }
28420     }
28421     
28422     if(this.adjustments == "auto"){
28423         var rc = this.resizeChild;
28424         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
28425         if(rc && (hw || hn)){
28426             rc.position("relative");
28427             rc.setLeft(hw ? hw.el.getWidth() : 0);
28428             rc.setTop(hn ? hn.el.getHeight() : 0);
28429         }
28430         this.adjustments = [
28431             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
28432             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
28433         ];
28434     }
28435
28436     if(this.draggable){
28437         this.dd = this.dynamic ?
28438             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
28439         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
28440     }
28441
28442     // public events
28443     this.addEvents({
28444         /**
28445          * @event beforeresize
28446          * Fired before resize is allowed. Set enabled to false to cancel resize.
28447          * @param {Roo.Resizable} this
28448          * @param {Roo.EventObject} e The mousedown event
28449          */
28450         "beforeresize" : true,
28451         /**
28452          * @event resize
28453          * Fired after a resize.
28454          * @param {Roo.Resizable} this
28455          * @param {Number} width The new width
28456          * @param {Number} height The new height
28457          * @param {Roo.EventObject} e The mouseup event
28458          */
28459         "resize" : true
28460     });
28461
28462     if(this.width !== null && this.height !== null){
28463         this.resizeTo(this.width, this.height);
28464     }else{
28465         this.updateChildSize();
28466     }
28467     if(Roo.isIE){
28468         this.el.dom.style.zoom = 1;
28469     }
28470     Roo.Resizable.superclass.constructor.call(this);
28471 };
28472
28473 Roo.extend(Roo.Resizable, Roo.util.Observable, {
28474         resizeChild : false,
28475         adjustments : [0, 0],
28476         minWidth : 5,
28477         minHeight : 5,
28478         maxWidth : 10000,
28479         maxHeight : 10000,
28480         enabled : true,
28481         animate : false,
28482         duration : .35,
28483         dynamic : false,
28484         handles : false,
28485         multiDirectional : false,
28486         disableTrackOver : false,
28487         easing : 'easeOutStrong',
28488         widthIncrement : 0,
28489         heightIncrement : 0,
28490         pinned : false,
28491         width : null,
28492         height : null,
28493         preserveRatio : false,
28494         transparent: false,
28495         minX: 0,
28496         minY: 0,
28497         draggable: false,
28498
28499         /**
28500          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
28501          */
28502         constrainTo: undefined,
28503         /**
28504          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
28505          */
28506         resizeRegion: undefined,
28507
28508
28509     /**
28510      * Perform a manual resize
28511      * @param {Number} width
28512      * @param {Number} height
28513      */
28514     resizeTo : function(width, height){
28515         this.el.setSize(width, height);
28516         this.updateChildSize();
28517         this.fireEvent("resize", this, width, height, null);
28518     },
28519
28520     // private
28521     startSizing : function(e, handle){
28522         this.fireEvent("beforeresize", this, e);
28523         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
28524
28525             if(!this.overlay){
28526                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
28527                 this.overlay.unselectable();
28528                 this.overlay.enableDisplayMode("block");
28529                 this.overlay.on("mousemove", this.onMouseMove, this);
28530                 this.overlay.on("mouseup", this.onMouseUp, this);
28531             }
28532             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
28533
28534             this.resizing = true;
28535             this.startBox = this.el.getBox();
28536             this.startPoint = e.getXY();
28537             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
28538                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
28539
28540             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28541             this.overlay.show();
28542
28543             if(this.constrainTo) {
28544                 var ct = Roo.get(this.constrainTo);
28545                 this.resizeRegion = ct.getRegion().adjust(
28546                     ct.getFrameWidth('t'),
28547                     ct.getFrameWidth('l'),
28548                     -ct.getFrameWidth('b'),
28549                     -ct.getFrameWidth('r')
28550                 );
28551             }
28552
28553             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
28554             this.proxy.show();
28555             this.proxy.setBox(this.startBox);
28556             if(!this.dynamic){
28557                 this.proxy.setStyle('visibility', 'visible');
28558             }
28559         }
28560     },
28561
28562     // private
28563     onMouseDown : function(handle, e){
28564         if(this.enabled){
28565             e.stopEvent();
28566             this.activeHandle = handle;
28567             this.startSizing(e, handle);
28568         }
28569     },
28570
28571     // private
28572     onMouseUp : function(e){
28573         var size = this.resizeElement();
28574         this.resizing = false;
28575         this.handleOut();
28576         this.overlay.hide();
28577         this.proxy.hide();
28578         this.fireEvent("resize", this, size.width, size.height, e);
28579     },
28580
28581     // private
28582     updateChildSize : function(){
28583         if(this.resizeChild){
28584             var el = this.el;
28585             var child = this.resizeChild;
28586             var adj = this.adjustments;
28587             if(el.dom.offsetWidth){
28588                 var b = el.getSize(true);
28589                 child.setSize(b.width+adj[0], b.height+adj[1]);
28590             }
28591             // Second call here for IE
28592             // The first call enables instant resizing and
28593             // the second call corrects scroll bars if they
28594             // exist
28595             if(Roo.isIE){
28596                 setTimeout(function(){
28597                     if(el.dom.offsetWidth){
28598                         var b = el.getSize(true);
28599                         child.setSize(b.width+adj[0], b.height+adj[1]);
28600                     }
28601                 }, 10);
28602             }
28603         }
28604     },
28605
28606     // private
28607     snap : function(value, inc, min){
28608         if(!inc || !value) return value;
28609         var newValue = value;
28610         var m = value % inc;
28611         if(m > 0){
28612             if(m > (inc/2)){
28613                 newValue = value + (inc-m);
28614             }else{
28615                 newValue = value - m;
28616             }
28617         }
28618         return Math.max(min, newValue);
28619     },
28620
28621     // private
28622     resizeElement : function(){
28623         var box = this.proxy.getBox();
28624         if(this.updateBox){
28625             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28626         }else{
28627             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28628         }
28629         this.updateChildSize();
28630         if(!this.dynamic){
28631             this.proxy.hide();
28632         }
28633         return box;
28634     },
28635
28636     // private
28637     constrain : function(v, diff, m, mx){
28638         if(v - diff < m){
28639             diff = v - m;
28640         }else if(v - diff > mx){
28641             diff = mx - v;
28642         }
28643         return diff;
28644     },
28645
28646     // private
28647     onMouseMove : function(e){
28648         if(this.enabled){
28649             try{// try catch so if something goes wrong the user doesn't get hung
28650
28651             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28652                 return;
28653             }
28654
28655             //var curXY = this.startPoint;
28656             var curSize = this.curSize || this.startBox;
28657             var x = this.startBox.x, y = this.startBox.y;
28658             var ox = x, oy = y;
28659             var w = curSize.width, h = curSize.height;
28660             var ow = w, oh = h;
28661             var mw = this.minWidth, mh = this.minHeight;
28662             var mxw = this.maxWidth, mxh = this.maxHeight;
28663             var wi = this.widthIncrement;
28664             var hi = this.heightIncrement;
28665
28666             var eventXY = e.getXY();
28667             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28668             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28669
28670             var pos = this.activeHandle.position;
28671
28672             switch(pos){
28673                 case "east":
28674                     w += diffX;
28675                     w = Math.min(Math.max(mw, w), mxw);
28676                     break;
28677              
28678                 case "south":
28679                     h += diffY;
28680                     h = Math.min(Math.max(mh, h), mxh);
28681                     break;
28682                 case "southeast":
28683                     w += diffX;
28684                     h += diffY;
28685                     w = Math.min(Math.max(mw, w), mxw);
28686                     h = Math.min(Math.max(mh, h), mxh);
28687                     break;
28688                 case "north":
28689                     diffY = this.constrain(h, diffY, mh, mxh);
28690                     y += diffY;
28691                     h -= diffY;
28692                     break;
28693                 case "hdrag":
28694                     
28695                     if (wi) {
28696                         var adiffX = Math.abs(diffX);
28697                         var sub = (adiffX % wi); // how much 
28698                         if (sub > (wi/2)) { // far enough to snap
28699                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28700                         } else {
28701                             // remove difference.. 
28702                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28703                         }
28704                     }
28705                     x += diffX;
28706                     x = Math.max(this.minX, x);
28707                     break;
28708                 case "west":
28709                     diffX = this.constrain(w, diffX, mw, mxw);
28710                     x += diffX;
28711                     w -= diffX;
28712                     break;
28713                 case "northeast":
28714                     w += diffX;
28715                     w = Math.min(Math.max(mw, w), mxw);
28716                     diffY = this.constrain(h, diffY, mh, mxh);
28717                     y += diffY;
28718                     h -= diffY;
28719                     break;
28720                 case "northwest":
28721                     diffX = this.constrain(w, diffX, mw, mxw);
28722                     diffY = this.constrain(h, diffY, mh, mxh);
28723                     y += diffY;
28724                     h -= diffY;
28725                     x += diffX;
28726                     w -= diffX;
28727                     break;
28728                case "southwest":
28729                     diffX = this.constrain(w, diffX, mw, mxw);
28730                     h += diffY;
28731                     h = Math.min(Math.max(mh, h), mxh);
28732                     x += diffX;
28733                     w -= diffX;
28734                     break;
28735             }
28736
28737             var sw = this.snap(w, wi, mw);
28738             var sh = this.snap(h, hi, mh);
28739             if(sw != w || sh != h){
28740                 switch(pos){
28741                     case "northeast":
28742                         y -= sh - h;
28743                     break;
28744                     case "north":
28745                         y -= sh - h;
28746                         break;
28747                     case "southwest":
28748                         x -= sw - w;
28749                     break;
28750                     case "west":
28751                         x -= sw - w;
28752                         break;
28753                     case "northwest":
28754                         x -= sw - w;
28755                         y -= sh - h;
28756                     break;
28757                 }
28758                 w = sw;
28759                 h = sh;
28760             }
28761
28762             if(this.preserveRatio){
28763                 switch(pos){
28764                     case "southeast":
28765                     case "east":
28766                         h = oh * (w/ow);
28767                         h = Math.min(Math.max(mh, h), mxh);
28768                         w = ow * (h/oh);
28769                        break;
28770                     case "south":
28771                         w = ow * (h/oh);
28772                         w = Math.min(Math.max(mw, w), mxw);
28773                         h = oh * (w/ow);
28774                         break;
28775                     case "northeast":
28776                         w = ow * (h/oh);
28777                         w = Math.min(Math.max(mw, w), mxw);
28778                         h = oh * (w/ow);
28779                     break;
28780                     case "north":
28781                         var tw = w;
28782                         w = ow * (h/oh);
28783                         w = Math.min(Math.max(mw, w), mxw);
28784                         h = oh * (w/ow);
28785                         x += (tw - w) / 2;
28786                         break;
28787                     case "southwest":
28788                         h = oh * (w/ow);
28789                         h = Math.min(Math.max(mh, h), mxh);
28790                         var tw = w;
28791                         w = ow * (h/oh);
28792                         x += tw - w;
28793                         break;
28794                     case "west":
28795                         var th = h;
28796                         h = oh * (w/ow);
28797                         h = Math.min(Math.max(mh, h), mxh);
28798                         y += (th - h) / 2;
28799                         var tw = w;
28800                         w = ow * (h/oh);
28801                         x += tw - w;
28802                        break;
28803                     case "northwest":
28804                         var tw = w;
28805                         var th = h;
28806                         h = oh * (w/ow);
28807                         h = Math.min(Math.max(mh, h), mxh);
28808                         w = ow * (h/oh);
28809                         y += th - h;
28810                         x += tw - w;
28811                        break;
28812
28813                 }
28814             }
28815             if (pos == 'hdrag') {
28816                 w = ow;
28817             }
28818             this.proxy.setBounds(x, y, w, h);
28819             if(this.dynamic){
28820                 this.resizeElement();
28821             }
28822             }catch(e){}
28823         }
28824     },
28825
28826     // private
28827     handleOver : function(){
28828         if(this.enabled){
28829             this.el.addClass("x-resizable-over");
28830         }
28831     },
28832
28833     // private
28834     handleOut : function(){
28835         if(!this.resizing){
28836             this.el.removeClass("x-resizable-over");
28837         }
28838     },
28839
28840     /**
28841      * Returns the element this component is bound to.
28842      * @return {Roo.Element}
28843      */
28844     getEl : function(){
28845         return this.el;
28846     },
28847
28848     /**
28849      * Returns the resizeChild element (or null).
28850      * @return {Roo.Element}
28851      */
28852     getResizeChild : function(){
28853         return this.resizeChild;
28854     },
28855
28856     /**
28857      * Destroys this resizable. If the element was wrapped and
28858      * removeEl is not true then the element remains.
28859      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28860      */
28861     destroy : function(removeEl){
28862         this.proxy.remove();
28863         if(this.overlay){
28864             this.overlay.removeAllListeners();
28865             this.overlay.remove();
28866         }
28867         var ps = Roo.Resizable.positions;
28868         for(var k in ps){
28869             if(typeof ps[k] != "function" && this[ps[k]]){
28870                 var h = this[ps[k]];
28871                 h.el.removeAllListeners();
28872                 h.el.remove();
28873             }
28874         }
28875         if(removeEl){
28876             this.el.update("");
28877             this.el.remove();
28878         }
28879     }
28880 });
28881
28882 // private
28883 // hash to map config positions to true positions
28884 Roo.Resizable.positions = {
28885     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28886     hd: "hdrag"
28887 };
28888
28889 // private
28890 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28891     if(!this.tpl){
28892         // only initialize the template if resizable is used
28893         var tpl = Roo.DomHelper.createTemplate(
28894             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28895         );
28896         tpl.compile();
28897         Roo.Resizable.Handle.prototype.tpl = tpl;
28898     }
28899     this.position = pos;
28900     this.rz = rz;
28901     // show north drag fro topdra
28902     var handlepos = pos == 'hdrag' ? 'north' : pos;
28903     
28904     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28905     if (pos == 'hdrag') {
28906         this.el.setStyle('cursor', 'pointer');
28907     }
28908     this.el.unselectable();
28909     if(transparent){
28910         this.el.setOpacity(0);
28911     }
28912     this.el.on("mousedown", this.onMouseDown, this);
28913     if(!disableTrackOver){
28914         this.el.on("mouseover", this.onMouseOver, this);
28915         this.el.on("mouseout", this.onMouseOut, this);
28916     }
28917 };
28918
28919 // private
28920 Roo.Resizable.Handle.prototype = {
28921     afterResize : function(rz){
28922         // do nothing
28923     },
28924     // private
28925     onMouseDown : function(e){
28926         this.rz.onMouseDown(this, e);
28927     },
28928     // private
28929     onMouseOver : function(e){
28930         this.rz.handleOver(this, e);
28931     },
28932     // private
28933     onMouseOut : function(e){
28934         this.rz.handleOut(this, e);
28935     }
28936 };/*
28937  * Based on:
28938  * Ext JS Library 1.1.1
28939  * Copyright(c) 2006-2007, Ext JS, LLC.
28940  *
28941  * Originally Released Under LGPL - original licence link has changed is not relivant.
28942  *
28943  * Fork - LGPL
28944  * <script type="text/javascript">
28945  */
28946
28947 /**
28948  * @class Roo.Editor
28949  * @extends Roo.Component
28950  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28951  * @constructor
28952  * Create a new Editor
28953  * @param {Roo.form.Field} field The Field object (or descendant)
28954  * @param {Object} config The config object
28955  */
28956 Roo.Editor = function(field, config){
28957     Roo.Editor.superclass.constructor.call(this, config);
28958     this.field = field;
28959     this.addEvents({
28960         /**
28961              * @event beforestartedit
28962              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28963              * false from the handler of this event.
28964              * @param {Editor} this
28965              * @param {Roo.Element} boundEl The underlying element bound to this editor
28966              * @param {Mixed} value The field value being set
28967              */
28968         "beforestartedit" : true,
28969         /**
28970              * @event startedit
28971              * Fires when this editor is displayed
28972              * @param {Roo.Element} boundEl The underlying element bound to this editor
28973              * @param {Mixed} value The starting field value
28974              */
28975         "startedit" : true,
28976         /**
28977              * @event beforecomplete
28978              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28979              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28980              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28981              * event will not fire since no edit actually occurred.
28982              * @param {Editor} this
28983              * @param {Mixed} value The current field value
28984              * @param {Mixed} startValue The original field value
28985              */
28986         "beforecomplete" : true,
28987         /**
28988              * @event complete
28989              * Fires after editing is complete and any changed value has been written to the underlying field.
28990              * @param {Editor} this
28991              * @param {Mixed} value The current field value
28992              * @param {Mixed} startValue The original field value
28993              */
28994         "complete" : true,
28995         /**
28996          * @event specialkey
28997          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28998          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28999          * @param {Roo.form.Field} this
29000          * @param {Roo.EventObject} e The event object
29001          */
29002         "specialkey" : true
29003     });
29004 };
29005
29006 Roo.extend(Roo.Editor, Roo.Component, {
29007     /**
29008      * @cfg {Boolean/String} autosize
29009      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29010      * or "height" to adopt the height only (defaults to false)
29011      */
29012     /**
29013      * @cfg {Boolean} revertInvalid
29014      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29015      * validation fails (defaults to true)
29016      */
29017     /**
29018      * @cfg {Boolean} ignoreNoChange
29019      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29020      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29021      * will never be ignored.
29022      */
29023     /**
29024      * @cfg {Boolean} hideEl
29025      * False to keep the bound element visible while the editor is displayed (defaults to true)
29026      */
29027     /**
29028      * @cfg {Mixed} value
29029      * The data value of the underlying field (defaults to "")
29030      */
29031     value : "",
29032     /**
29033      * @cfg {String} alignment
29034      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29035      */
29036     alignment: "c-c?",
29037     /**
29038      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29039      * for bottom-right shadow (defaults to "frame")
29040      */
29041     shadow : "frame",
29042     /**
29043      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29044      */
29045     constrain : false,
29046     /**
29047      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29048      */
29049     completeOnEnter : false,
29050     /**
29051      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29052      */
29053     cancelOnEsc : false,
29054     /**
29055      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29056      */
29057     updateEl : false,
29058
29059     // private
29060     onRender : function(ct, position){
29061         this.el = new Roo.Layer({
29062             shadow: this.shadow,
29063             cls: "x-editor",
29064             parentEl : ct,
29065             shim : this.shim,
29066             shadowOffset:4,
29067             id: this.id,
29068             constrain: this.constrain
29069         });
29070         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29071         if(this.field.msgTarget != 'title'){
29072             this.field.msgTarget = 'qtip';
29073         }
29074         this.field.render(this.el);
29075         if(Roo.isGecko){
29076             this.field.el.dom.setAttribute('autocomplete', 'off');
29077         }
29078         this.field.on("specialkey", this.onSpecialKey, this);
29079         if(this.swallowKeys){
29080             this.field.el.swallowEvent(['keydown','keypress']);
29081         }
29082         this.field.show();
29083         this.field.on("blur", this.onBlur, this);
29084         if(this.field.grow){
29085             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29086         }
29087     },
29088
29089     onSpecialKey : function(field, e)
29090     {
29091         //Roo.log('editor onSpecialKey');
29092         if(this.completeOnEnter && e.getKey() == e.ENTER){
29093             e.stopEvent();
29094             this.completeEdit();
29095             return;
29096         }
29097         // do not fire special key otherwise it might hide close the editor...
29098         if(e.getKey() == e.ENTER){    
29099             return;
29100         }
29101         if(this.cancelOnEsc && e.getKey() == e.ESC){
29102             this.cancelEdit();
29103             return;
29104         } 
29105         this.fireEvent('specialkey', field, e);
29106     
29107     },
29108
29109     /**
29110      * Starts the editing process and shows the editor.
29111      * @param {String/HTMLElement/Element} el The element to edit
29112      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29113       * to the innerHTML of el.
29114      */
29115     startEdit : function(el, value){
29116         if(this.editing){
29117             this.completeEdit();
29118         }
29119         this.boundEl = Roo.get(el);
29120         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29121         if(!this.rendered){
29122             this.render(this.parentEl || document.body);
29123         }
29124         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29125             return;
29126         }
29127         this.startValue = v;
29128         this.field.setValue(v);
29129         if(this.autoSize){
29130             var sz = this.boundEl.getSize();
29131             switch(this.autoSize){
29132                 case "width":
29133                 this.setSize(sz.width,  "");
29134                 break;
29135                 case "height":
29136                 this.setSize("",  sz.height);
29137                 break;
29138                 default:
29139                 this.setSize(sz.width,  sz.height);
29140             }
29141         }
29142         this.el.alignTo(this.boundEl, this.alignment);
29143         this.editing = true;
29144         if(Roo.QuickTips){
29145             Roo.QuickTips.disable();
29146         }
29147         this.show();
29148     },
29149
29150     /**
29151      * Sets the height and width of this editor.
29152      * @param {Number} width The new width
29153      * @param {Number} height The new height
29154      */
29155     setSize : function(w, h){
29156         this.field.setSize(w, h);
29157         if(this.el){
29158             this.el.sync();
29159         }
29160     },
29161
29162     /**
29163      * Realigns the editor to the bound field based on the current alignment config value.
29164      */
29165     realign : function(){
29166         this.el.alignTo(this.boundEl, this.alignment);
29167     },
29168
29169     /**
29170      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29171      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29172      */
29173     completeEdit : function(remainVisible){
29174         if(!this.editing){
29175             return;
29176         }
29177         var v = this.getValue();
29178         if(this.revertInvalid !== false && !this.field.isValid()){
29179             v = this.startValue;
29180             this.cancelEdit(true);
29181         }
29182         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29183             this.editing = false;
29184             this.hide();
29185             return;
29186         }
29187         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29188             this.editing = false;
29189             if(this.updateEl && this.boundEl){
29190                 this.boundEl.update(v);
29191             }
29192             if(remainVisible !== true){
29193                 this.hide();
29194             }
29195             this.fireEvent("complete", this, v, this.startValue);
29196         }
29197     },
29198
29199     // private
29200     onShow : function(){
29201         this.el.show();
29202         if(this.hideEl !== false){
29203             this.boundEl.hide();
29204         }
29205         this.field.show();
29206         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29207             this.fixIEFocus = true;
29208             this.deferredFocus.defer(50, this);
29209         }else{
29210             this.field.focus();
29211         }
29212         this.fireEvent("startedit", this.boundEl, this.startValue);
29213     },
29214
29215     deferredFocus : function(){
29216         if(this.editing){
29217             this.field.focus();
29218         }
29219     },
29220
29221     /**
29222      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29223      * reverted to the original starting value.
29224      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29225      * cancel (defaults to false)
29226      */
29227     cancelEdit : function(remainVisible){
29228         if(this.editing){
29229             this.setValue(this.startValue);
29230             if(remainVisible !== true){
29231                 this.hide();
29232             }
29233         }
29234     },
29235
29236     // private
29237     onBlur : function(){
29238         if(this.allowBlur !== true && this.editing){
29239             this.completeEdit();
29240         }
29241     },
29242
29243     // private
29244     onHide : function(){
29245         if(this.editing){
29246             this.completeEdit();
29247             return;
29248         }
29249         this.field.blur();
29250         if(this.field.collapse){
29251             this.field.collapse();
29252         }
29253         this.el.hide();
29254         if(this.hideEl !== false){
29255             this.boundEl.show();
29256         }
29257         if(Roo.QuickTips){
29258             Roo.QuickTips.enable();
29259         }
29260     },
29261
29262     /**
29263      * Sets the data value of the editor
29264      * @param {Mixed} value Any valid value supported by the underlying field
29265      */
29266     setValue : function(v){
29267         this.field.setValue(v);
29268     },
29269
29270     /**
29271      * Gets the data value of the editor
29272      * @return {Mixed} The data value
29273      */
29274     getValue : function(){
29275         return this.field.getValue();
29276     }
29277 });/*
29278  * Based on:
29279  * Ext JS Library 1.1.1
29280  * Copyright(c) 2006-2007, Ext JS, LLC.
29281  *
29282  * Originally Released Under LGPL - original licence link has changed is not relivant.
29283  *
29284  * Fork - LGPL
29285  * <script type="text/javascript">
29286  */
29287  
29288 /**
29289  * @class Roo.BasicDialog
29290  * @extends Roo.util.Observable
29291  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29292  * <pre><code>
29293 var dlg = new Roo.BasicDialog("my-dlg", {
29294     height: 200,
29295     width: 300,
29296     minHeight: 100,
29297     minWidth: 150,
29298     modal: true,
29299     proxyDrag: true,
29300     shadow: true
29301 });
29302 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29303 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29304 dlg.addButton('Cancel', dlg.hide, dlg);
29305 dlg.show();
29306 </code></pre>
29307   <b>A Dialog should always be a direct child of the body element.</b>
29308  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29309  * @cfg {String} title Default text to display in the title bar (defaults to null)
29310  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29311  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29312  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29313  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29314  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29315  * (defaults to null with no animation)
29316  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29317  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29318  * property for valid values (defaults to 'all')
29319  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29320  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29321  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29322  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29323  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29324  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29325  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29326  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29327  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29328  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29329  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29330  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
29331  * draggable = true (defaults to false)
29332  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
29333  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29334  * shadow (defaults to false)
29335  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
29336  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
29337  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
29338  * @cfg {Array} buttons Array of buttons
29339  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
29340  * @constructor
29341  * Create a new BasicDialog.
29342  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
29343  * @param {Object} config Configuration options
29344  */
29345 Roo.BasicDialog = function(el, config){
29346     this.el = Roo.get(el);
29347     var dh = Roo.DomHelper;
29348     if(!this.el && config && config.autoCreate){
29349         if(typeof config.autoCreate == "object"){
29350             if(!config.autoCreate.id){
29351                 config.autoCreate.id = el;
29352             }
29353             this.el = dh.append(document.body,
29354                         config.autoCreate, true);
29355         }else{
29356             this.el = dh.append(document.body,
29357                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
29358         }
29359     }
29360     el = this.el;
29361     el.setDisplayed(true);
29362     el.hide = this.hideAction;
29363     this.id = el.id;
29364     el.addClass("x-dlg");
29365
29366     Roo.apply(this, config);
29367
29368     this.proxy = el.createProxy("x-dlg-proxy");
29369     this.proxy.hide = this.hideAction;
29370     this.proxy.setOpacity(.5);
29371     this.proxy.hide();
29372
29373     if(config.width){
29374         el.setWidth(config.width);
29375     }
29376     if(config.height){
29377         el.setHeight(config.height);
29378     }
29379     this.size = el.getSize();
29380     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
29381         this.xy = [config.x,config.y];
29382     }else{
29383         this.xy = el.getCenterXY(true);
29384     }
29385     /** The header element @type Roo.Element */
29386     this.header = el.child("> .x-dlg-hd");
29387     /** The body element @type Roo.Element */
29388     this.body = el.child("> .x-dlg-bd");
29389     /** The footer element @type Roo.Element */
29390     this.footer = el.child("> .x-dlg-ft");
29391
29392     if(!this.header){
29393         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
29394     }
29395     if(!this.body){
29396         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
29397     }
29398
29399     this.header.unselectable();
29400     if(this.title){
29401         this.header.update(this.title);
29402     }
29403     // this element allows the dialog to be focused for keyboard event
29404     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
29405     this.focusEl.swallowEvent("click", true);
29406
29407     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
29408
29409     // wrap the body and footer for special rendering
29410     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
29411     if(this.footer){
29412         this.bwrap.dom.appendChild(this.footer.dom);
29413     }
29414
29415     this.bg = this.el.createChild({
29416         tag: "div", cls:"x-dlg-bg",
29417         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
29418     });
29419     this.centerBg = this.bg.child("div.x-dlg-bg-center");
29420
29421
29422     if(this.autoScroll !== false && !this.autoTabs){
29423         this.body.setStyle("overflow", "auto");
29424     }
29425
29426     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
29427
29428     if(this.closable !== false){
29429         this.el.addClass("x-dlg-closable");
29430         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
29431         this.close.on("click", this.closeClick, this);
29432         this.close.addClassOnOver("x-dlg-close-over");
29433     }
29434     if(this.collapsible !== false){
29435         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
29436         this.collapseBtn.on("click", this.collapseClick, this);
29437         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
29438         this.header.on("dblclick", this.collapseClick, this);
29439     }
29440     if(this.resizable !== false){
29441         this.el.addClass("x-dlg-resizable");
29442         this.resizer = new Roo.Resizable(el, {
29443             minWidth: this.minWidth || 80,
29444             minHeight:this.minHeight || 80,
29445             handles: this.resizeHandles || "all",
29446             pinned: true
29447         });
29448         this.resizer.on("beforeresize", this.beforeResize, this);
29449         this.resizer.on("resize", this.onResize, this);
29450     }
29451     if(this.draggable !== false){
29452         el.addClass("x-dlg-draggable");
29453         if (!this.proxyDrag) {
29454             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
29455         }
29456         else {
29457             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
29458         }
29459         dd.setHandleElId(this.header.id);
29460         dd.endDrag = this.endMove.createDelegate(this);
29461         dd.startDrag = this.startMove.createDelegate(this);
29462         dd.onDrag = this.onDrag.createDelegate(this);
29463         dd.scroll = false;
29464         this.dd = dd;
29465     }
29466     if(this.modal){
29467         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
29468         this.mask.enableDisplayMode("block");
29469         this.mask.hide();
29470         this.el.addClass("x-dlg-modal");
29471     }
29472     if(this.shadow){
29473         this.shadow = new Roo.Shadow({
29474             mode : typeof this.shadow == "string" ? this.shadow : "sides",
29475             offset : this.shadowOffset
29476         });
29477     }else{
29478         this.shadowOffset = 0;
29479     }
29480     if(Roo.useShims && this.shim !== false){
29481         this.shim = this.el.createShim();
29482         this.shim.hide = this.hideAction;
29483         this.shim.hide();
29484     }else{
29485         this.shim = false;
29486     }
29487     if(this.autoTabs){
29488         this.initTabs();
29489     }
29490     if (this.buttons) { 
29491         var bts= this.buttons;
29492         this.buttons = [];
29493         Roo.each(bts, function(b) {
29494             this.addButton(b);
29495         }, this);
29496     }
29497     
29498     
29499     this.addEvents({
29500         /**
29501          * @event keydown
29502          * Fires when a key is pressed
29503          * @param {Roo.BasicDialog} this
29504          * @param {Roo.EventObject} e
29505          */
29506         "keydown" : true,
29507         /**
29508          * @event move
29509          * Fires when this dialog is moved by the user.
29510          * @param {Roo.BasicDialog} this
29511          * @param {Number} x The new page X
29512          * @param {Number} y The new page Y
29513          */
29514         "move" : true,
29515         /**
29516          * @event resize
29517          * Fires when this dialog is resized by the user.
29518          * @param {Roo.BasicDialog} this
29519          * @param {Number} width The new width
29520          * @param {Number} height The new height
29521          */
29522         "resize" : true,
29523         /**
29524          * @event beforehide
29525          * Fires before this dialog is hidden.
29526          * @param {Roo.BasicDialog} this
29527          */
29528         "beforehide" : true,
29529         /**
29530          * @event hide
29531          * Fires when this dialog is hidden.
29532          * @param {Roo.BasicDialog} this
29533          */
29534         "hide" : true,
29535         /**
29536          * @event beforeshow
29537          * Fires before this dialog is shown.
29538          * @param {Roo.BasicDialog} this
29539          */
29540         "beforeshow" : true,
29541         /**
29542          * @event show
29543          * Fires when this dialog is shown.
29544          * @param {Roo.BasicDialog} this
29545          */
29546         "show" : true
29547     });
29548     el.on("keydown", this.onKeyDown, this);
29549     el.on("mousedown", this.toFront, this);
29550     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
29551     this.el.hide();
29552     Roo.DialogManager.register(this);
29553     Roo.BasicDialog.superclass.constructor.call(this);
29554 };
29555
29556 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29557     shadowOffset: Roo.isIE ? 6 : 5,
29558     minHeight: 80,
29559     minWidth: 200,
29560     minButtonWidth: 75,
29561     defaultButton: null,
29562     buttonAlign: "right",
29563     tabTag: 'div',
29564     firstShow: true,
29565
29566     /**
29567      * Sets the dialog title text
29568      * @param {String} text The title text to display
29569      * @return {Roo.BasicDialog} this
29570      */
29571     setTitle : function(text){
29572         this.header.update(text);
29573         return this;
29574     },
29575
29576     // private
29577     closeClick : function(){
29578         this.hide();
29579     },
29580
29581     // private
29582     collapseClick : function(){
29583         this[this.collapsed ? "expand" : "collapse"]();
29584     },
29585
29586     /**
29587      * Collapses the dialog to its minimized state (only the title bar is visible).
29588      * Equivalent to the user clicking the collapse dialog button.
29589      */
29590     collapse : function(){
29591         if(!this.collapsed){
29592             this.collapsed = true;
29593             this.el.addClass("x-dlg-collapsed");
29594             this.restoreHeight = this.el.getHeight();
29595             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29596         }
29597     },
29598
29599     /**
29600      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29601      * clicking the expand dialog button.
29602      */
29603     expand : function(){
29604         if(this.collapsed){
29605             this.collapsed = false;
29606             this.el.removeClass("x-dlg-collapsed");
29607             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29608         }
29609     },
29610
29611     /**
29612      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29613      * @return {Roo.TabPanel} The tabs component
29614      */
29615     initTabs : function(){
29616         var tabs = this.getTabs();
29617         while(tabs.getTab(0)){
29618             tabs.removeTab(0);
29619         }
29620         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29621             var dom = el.dom;
29622             tabs.addTab(Roo.id(dom), dom.title);
29623             dom.title = "";
29624         });
29625         tabs.activate(0);
29626         return tabs;
29627     },
29628
29629     // private
29630     beforeResize : function(){
29631         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29632     },
29633
29634     // private
29635     onResize : function(){
29636         this.refreshSize();
29637         this.syncBodyHeight();
29638         this.adjustAssets();
29639         this.focus();
29640         this.fireEvent("resize", this, this.size.width, this.size.height);
29641     },
29642
29643     // private
29644     onKeyDown : function(e){
29645         if(this.isVisible()){
29646             this.fireEvent("keydown", this, e);
29647         }
29648     },
29649
29650     /**
29651      * Resizes the dialog.
29652      * @param {Number} width
29653      * @param {Number} height
29654      * @return {Roo.BasicDialog} this
29655      */
29656     resizeTo : function(width, height){
29657         this.el.setSize(width, height);
29658         this.size = {width: width, height: height};
29659         this.syncBodyHeight();
29660         if(this.fixedcenter){
29661             this.center();
29662         }
29663         if(this.isVisible()){
29664             this.constrainXY();
29665             this.adjustAssets();
29666         }
29667         this.fireEvent("resize", this, width, height);
29668         return this;
29669     },
29670
29671
29672     /**
29673      * Resizes the dialog to fit the specified content size.
29674      * @param {Number} width
29675      * @param {Number} height
29676      * @return {Roo.BasicDialog} this
29677      */
29678     setContentSize : function(w, h){
29679         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29680         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29681         //if(!this.el.isBorderBox()){
29682             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29683             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29684         //}
29685         if(this.tabs){
29686             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29687             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29688         }
29689         this.resizeTo(w, h);
29690         return this;
29691     },
29692
29693     /**
29694      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29695      * executed in response to a particular key being pressed while the dialog is active.
29696      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29697      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29698      * @param {Function} fn The function to call
29699      * @param {Object} scope (optional) The scope of the function
29700      * @return {Roo.BasicDialog} this
29701      */
29702     addKeyListener : function(key, fn, scope){
29703         var keyCode, shift, ctrl, alt;
29704         if(typeof key == "object" && !(key instanceof Array)){
29705             keyCode = key["key"];
29706             shift = key["shift"];
29707             ctrl = key["ctrl"];
29708             alt = key["alt"];
29709         }else{
29710             keyCode = key;
29711         }
29712         var handler = function(dlg, e){
29713             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29714                 var k = e.getKey();
29715                 if(keyCode instanceof Array){
29716                     for(var i = 0, len = keyCode.length; i < len; i++){
29717                         if(keyCode[i] == k){
29718                           fn.call(scope || window, dlg, k, e);
29719                           return;
29720                         }
29721                     }
29722                 }else{
29723                     if(k == keyCode){
29724                         fn.call(scope || window, dlg, k, e);
29725                     }
29726                 }
29727             }
29728         };
29729         this.on("keydown", handler);
29730         return this;
29731     },
29732
29733     /**
29734      * Returns the TabPanel component (creates it if it doesn't exist).
29735      * Note: If you wish to simply check for the existence of tabs without creating them,
29736      * check for a null 'tabs' property.
29737      * @return {Roo.TabPanel} The tabs component
29738      */
29739     getTabs : function(){
29740         if(!this.tabs){
29741             this.el.addClass("x-dlg-auto-tabs");
29742             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29743             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29744         }
29745         return this.tabs;
29746     },
29747
29748     /**
29749      * Adds a button to the footer section of the dialog.
29750      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29751      * object or a valid Roo.DomHelper element config
29752      * @param {Function} handler The function called when the button is clicked
29753      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29754      * @return {Roo.Button} The new button
29755      */
29756     addButton : function(config, handler, scope){
29757         var dh = Roo.DomHelper;
29758         if(!this.footer){
29759             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29760         }
29761         if(!this.btnContainer){
29762             var tb = this.footer.createChild({
29763
29764                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29765                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29766             }, null, true);
29767             this.btnContainer = tb.firstChild.firstChild.firstChild;
29768         }
29769         var bconfig = {
29770             handler: handler,
29771             scope: scope,
29772             minWidth: this.minButtonWidth,
29773             hideParent:true
29774         };
29775         if(typeof config == "string"){
29776             bconfig.text = config;
29777         }else{
29778             if(config.tag){
29779                 bconfig.dhconfig = config;
29780             }else{
29781                 Roo.apply(bconfig, config);
29782             }
29783         }
29784         var fc = false;
29785         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29786             bconfig.position = Math.max(0, bconfig.position);
29787             fc = this.btnContainer.childNodes[bconfig.position];
29788         }
29789          
29790         var btn = new Roo.Button(
29791             fc ? 
29792                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29793                 : this.btnContainer.appendChild(document.createElement("td")),
29794             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29795             bconfig
29796         );
29797         this.syncBodyHeight();
29798         if(!this.buttons){
29799             /**
29800              * Array of all the buttons that have been added to this dialog via addButton
29801              * @type Array
29802              */
29803             this.buttons = [];
29804         }
29805         this.buttons.push(btn);
29806         return btn;
29807     },
29808
29809     /**
29810      * Sets the default button to be focused when the dialog is displayed.
29811      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29812      * @return {Roo.BasicDialog} this
29813      */
29814     setDefaultButton : function(btn){
29815         this.defaultButton = btn;
29816         return this;
29817     },
29818
29819     // private
29820     getHeaderFooterHeight : function(safe){
29821         var height = 0;
29822         if(this.header){
29823            height += this.header.getHeight();
29824         }
29825         if(this.footer){
29826            var fm = this.footer.getMargins();
29827             height += (this.footer.getHeight()+fm.top+fm.bottom);
29828         }
29829         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29830         height += this.centerBg.getPadding("tb");
29831         return height;
29832     },
29833
29834     // private
29835     syncBodyHeight : function()
29836     {
29837         var bd = this.body, // the text
29838             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
29839             bw = this.bwrap;
29840         var height = this.size.height - this.getHeaderFooterHeight(false);
29841         bd.setHeight(height-bd.getMargins("tb"));
29842         var hh = this.header.getHeight();
29843         var h = this.size.height-hh;
29844         cb.setHeight(h);
29845         
29846         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29847         bw.setHeight(h-cb.getPadding("tb"));
29848         
29849         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29850         bd.setWidth(bw.getWidth(true));
29851         if(this.tabs){
29852             this.tabs.syncHeight();
29853             if(Roo.isIE){
29854                 this.tabs.el.repaint();
29855             }
29856         }
29857     },
29858
29859     /**
29860      * Restores the previous state of the dialog if Roo.state is configured.
29861      * @return {Roo.BasicDialog} this
29862      */
29863     restoreState : function(){
29864         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29865         if(box && box.width){
29866             this.xy = [box.x, box.y];
29867             this.resizeTo(box.width, box.height);
29868         }
29869         return this;
29870     },
29871
29872     // private
29873     beforeShow : function(){
29874         this.expand();
29875         if(this.fixedcenter){
29876             this.xy = this.el.getCenterXY(true);
29877         }
29878         if(this.modal){
29879             Roo.get(document.body).addClass("x-body-masked");
29880             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29881             this.mask.show();
29882         }
29883         this.constrainXY();
29884     },
29885
29886     // private
29887     animShow : function(){
29888         var b = Roo.get(this.animateTarget).getBox();
29889         this.proxy.setSize(b.width, b.height);
29890         this.proxy.setLocation(b.x, b.y);
29891         this.proxy.show();
29892         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29893                     true, .35, this.showEl.createDelegate(this));
29894     },
29895
29896     /**
29897      * Shows the dialog.
29898      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29899      * @return {Roo.BasicDialog} this
29900      */
29901     show : function(animateTarget){
29902         if (this.fireEvent("beforeshow", this) === false){
29903             return;
29904         }
29905         if(this.syncHeightBeforeShow){
29906             this.syncBodyHeight();
29907         }else if(this.firstShow){
29908             this.firstShow = false;
29909             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29910         }
29911         this.animateTarget = animateTarget || this.animateTarget;
29912         if(!this.el.isVisible()){
29913             this.beforeShow();
29914             if(this.animateTarget && Roo.get(this.animateTarget)){
29915                 this.animShow();
29916             }else{
29917                 this.showEl();
29918             }
29919         }
29920         return this;
29921     },
29922
29923     // private
29924     showEl : function(){
29925         this.proxy.hide();
29926         this.el.setXY(this.xy);
29927         this.el.show();
29928         this.adjustAssets(true);
29929         this.toFront();
29930         this.focus();
29931         // IE peekaboo bug - fix found by Dave Fenwick
29932         if(Roo.isIE){
29933             this.el.repaint();
29934         }
29935         this.fireEvent("show", this);
29936     },
29937
29938     /**
29939      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29940      * dialog itself will receive focus.
29941      */
29942     focus : function(){
29943         if(this.defaultButton){
29944             this.defaultButton.focus();
29945         }else{
29946             this.focusEl.focus();
29947         }
29948     },
29949
29950     // private
29951     constrainXY : function(){
29952         if(this.constraintoviewport !== false){
29953             if(!this.viewSize){
29954                 if(this.container){
29955                     var s = this.container.getSize();
29956                     this.viewSize = [s.width, s.height];
29957                 }else{
29958                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29959                 }
29960             }
29961             var s = Roo.get(this.container||document).getScroll();
29962
29963             var x = this.xy[0], y = this.xy[1];
29964             var w = this.size.width, h = this.size.height;
29965             var vw = this.viewSize[0], vh = this.viewSize[1];
29966             // only move it if it needs it
29967             var moved = false;
29968             // first validate right/bottom
29969             if(x + w > vw+s.left){
29970                 x = vw - w;
29971                 moved = true;
29972             }
29973             if(y + h > vh+s.top){
29974                 y = vh - h;
29975                 moved = true;
29976             }
29977             // then make sure top/left isn't negative
29978             if(x < s.left){
29979                 x = s.left;
29980                 moved = true;
29981             }
29982             if(y < s.top){
29983                 y = s.top;
29984                 moved = true;
29985             }
29986             if(moved){
29987                 // cache xy
29988                 this.xy = [x, y];
29989                 if(this.isVisible()){
29990                     this.el.setLocation(x, y);
29991                     this.adjustAssets();
29992                 }
29993             }
29994         }
29995     },
29996
29997     // private
29998     onDrag : function(){
29999         if(!this.proxyDrag){
30000             this.xy = this.el.getXY();
30001             this.adjustAssets();
30002         }
30003     },
30004
30005     // private
30006     adjustAssets : function(doShow){
30007         var x = this.xy[0], y = this.xy[1];
30008         var w = this.size.width, h = this.size.height;
30009         if(doShow === true){
30010             if(this.shadow){
30011                 this.shadow.show(this.el);
30012             }
30013             if(this.shim){
30014                 this.shim.show();
30015             }
30016         }
30017         if(this.shadow && this.shadow.isVisible()){
30018             this.shadow.show(this.el);
30019         }
30020         if(this.shim && this.shim.isVisible()){
30021             this.shim.setBounds(x, y, w, h);
30022         }
30023     },
30024
30025     // private
30026     adjustViewport : function(w, h){
30027         if(!w || !h){
30028             w = Roo.lib.Dom.getViewWidth();
30029             h = Roo.lib.Dom.getViewHeight();
30030         }
30031         // cache the size
30032         this.viewSize = [w, h];
30033         if(this.modal && this.mask.isVisible()){
30034             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30035             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30036         }
30037         if(this.isVisible()){
30038             this.constrainXY();
30039         }
30040     },
30041
30042     /**
30043      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30044      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30045      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30046      */
30047     destroy : function(removeEl){
30048         if(this.isVisible()){
30049             this.animateTarget = null;
30050             this.hide();
30051         }
30052         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30053         if(this.tabs){
30054             this.tabs.destroy(removeEl);
30055         }
30056         Roo.destroy(
30057              this.shim,
30058              this.proxy,
30059              this.resizer,
30060              this.close,
30061              this.mask
30062         );
30063         if(this.dd){
30064             this.dd.unreg();
30065         }
30066         if(this.buttons){
30067            for(var i = 0, len = this.buttons.length; i < len; i++){
30068                this.buttons[i].destroy();
30069            }
30070         }
30071         this.el.removeAllListeners();
30072         if(removeEl === true){
30073             this.el.update("");
30074             this.el.remove();
30075         }
30076         Roo.DialogManager.unregister(this);
30077     },
30078
30079     // private
30080     startMove : function(){
30081         if(this.proxyDrag){
30082             this.proxy.show();
30083         }
30084         if(this.constraintoviewport !== false){
30085             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30086         }
30087     },
30088
30089     // private
30090     endMove : function(){
30091         if(!this.proxyDrag){
30092             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30093         }else{
30094             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30095             this.proxy.hide();
30096         }
30097         this.refreshSize();
30098         this.adjustAssets();
30099         this.focus();
30100         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30101     },
30102
30103     /**
30104      * Brings this dialog to the front of any other visible dialogs
30105      * @return {Roo.BasicDialog} this
30106      */
30107     toFront : function(){
30108         Roo.DialogManager.bringToFront(this);
30109         return this;
30110     },
30111
30112     /**
30113      * Sends this dialog to the back (under) of any other visible dialogs
30114      * @return {Roo.BasicDialog} this
30115      */
30116     toBack : function(){
30117         Roo.DialogManager.sendToBack(this);
30118         return this;
30119     },
30120
30121     /**
30122      * Centers this dialog in the viewport
30123      * @return {Roo.BasicDialog} this
30124      */
30125     center : function(){
30126         var xy = this.el.getCenterXY(true);
30127         this.moveTo(xy[0], xy[1]);
30128         return this;
30129     },
30130
30131     /**
30132      * Moves the dialog's top-left corner to the specified point
30133      * @param {Number} x
30134      * @param {Number} y
30135      * @return {Roo.BasicDialog} this
30136      */
30137     moveTo : function(x, y){
30138         this.xy = [x,y];
30139         if(this.isVisible()){
30140             this.el.setXY(this.xy);
30141             this.adjustAssets();
30142         }
30143         return this;
30144     },
30145
30146     /**
30147      * Aligns the dialog to the specified element
30148      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30149      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30150      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30151      * @return {Roo.BasicDialog} this
30152      */
30153     alignTo : function(element, position, offsets){
30154         this.xy = this.el.getAlignToXY(element, position, offsets);
30155         if(this.isVisible()){
30156             this.el.setXY(this.xy);
30157             this.adjustAssets();
30158         }
30159         return this;
30160     },
30161
30162     /**
30163      * Anchors an element to another element and realigns it when the window is resized.
30164      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30165      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30166      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30167      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30168      * is a number, it is used as the buffer delay (defaults to 50ms).
30169      * @return {Roo.BasicDialog} this
30170      */
30171     anchorTo : function(el, alignment, offsets, monitorScroll){
30172         var action = function(){
30173             this.alignTo(el, alignment, offsets);
30174         };
30175         Roo.EventManager.onWindowResize(action, this);
30176         var tm = typeof monitorScroll;
30177         if(tm != 'undefined'){
30178             Roo.EventManager.on(window, 'scroll', action, this,
30179                 {buffer: tm == 'number' ? monitorScroll : 50});
30180         }
30181         action.call(this);
30182         return this;
30183     },
30184
30185     /**
30186      * Returns true if the dialog is visible
30187      * @return {Boolean}
30188      */
30189     isVisible : function(){
30190         return this.el.isVisible();
30191     },
30192
30193     // private
30194     animHide : function(callback){
30195         var b = Roo.get(this.animateTarget).getBox();
30196         this.proxy.show();
30197         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30198         this.el.hide();
30199         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30200                     this.hideEl.createDelegate(this, [callback]));
30201     },
30202
30203     /**
30204      * Hides the dialog.
30205      * @param {Function} callback (optional) Function to call when the dialog is hidden
30206      * @return {Roo.BasicDialog} this
30207      */
30208     hide : function(callback){
30209         if (this.fireEvent("beforehide", this) === false){
30210             return;
30211         }
30212         if(this.shadow){
30213             this.shadow.hide();
30214         }
30215         if(this.shim) {
30216           this.shim.hide();
30217         }
30218         // sometimes animateTarget seems to get set.. causing problems...
30219         // this just double checks..
30220         if(this.animateTarget && Roo.get(this.animateTarget)) {
30221            this.animHide(callback);
30222         }else{
30223             this.el.hide();
30224             this.hideEl(callback);
30225         }
30226         return this;
30227     },
30228
30229     // private
30230     hideEl : function(callback){
30231         this.proxy.hide();
30232         if(this.modal){
30233             this.mask.hide();
30234             Roo.get(document.body).removeClass("x-body-masked");
30235         }
30236         this.fireEvent("hide", this);
30237         if(typeof callback == "function"){
30238             callback();
30239         }
30240     },
30241
30242     // private
30243     hideAction : function(){
30244         this.setLeft("-10000px");
30245         this.setTop("-10000px");
30246         this.setStyle("visibility", "hidden");
30247     },
30248
30249     // private
30250     refreshSize : function(){
30251         this.size = this.el.getSize();
30252         this.xy = this.el.getXY();
30253         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30254     },
30255
30256     // private
30257     // z-index is managed by the DialogManager and may be overwritten at any time
30258     setZIndex : function(index){
30259         if(this.modal){
30260             this.mask.setStyle("z-index", index);
30261         }
30262         if(this.shim){
30263             this.shim.setStyle("z-index", ++index);
30264         }
30265         if(this.shadow){
30266             this.shadow.setZIndex(++index);
30267         }
30268         this.el.setStyle("z-index", ++index);
30269         if(this.proxy){
30270             this.proxy.setStyle("z-index", ++index);
30271         }
30272         if(this.resizer){
30273             this.resizer.proxy.setStyle("z-index", ++index);
30274         }
30275
30276         this.lastZIndex = index;
30277     },
30278
30279     /**
30280      * Returns the element for this dialog
30281      * @return {Roo.Element} The underlying dialog Element
30282      */
30283     getEl : function(){
30284         return this.el;
30285     }
30286 });
30287
30288 /**
30289  * @class Roo.DialogManager
30290  * Provides global access to BasicDialogs that have been created and
30291  * support for z-indexing (layering) multiple open dialogs.
30292  */
30293 Roo.DialogManager = function(){
30294     var list = {};
30295     var accessList = [];
30296     var front = null;
30297
30298     // private
30299     var sortDialogs = function(d1, d2){
30300         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30301     };
30302
30303     // private
30304     var orderDialogs = function(){
30305         accessList.sort(sortDialogs);
30306         var seed = Roo.DialogManager.zseed;
30307         for(var i = 0, len = accessList.length; i < len; i++){
30308             var dlg = accessList[i];
30309             if(dlg){
30310                 dlg.setZIndex(seed + (i*10));
30311             }
30312         }
30313     };
30314
30315     return {
30316         /**
30317          * The starting z-index for BasicDialogs (defaults to 9000)
30318          * @type Number The z-index value
30319          */
30320         zseed : 9000,
30321
30322         // private
30323         register : function(dlg){
30324             list[dlg.id] = dlg;
30325             accessList.push(dlg);
30326         },
30327
30328         // private
30329         unregister : function(dlg){
30330             delete list[dlg.id];
30331             var i=0;
30332             var len=0;
30333             if(!accessList.indexOf){
30334                 for(  i = 0, len = accessList.length; i < len; i++){
30335                     if(accessList[i] == dlg){
30336                         accessList.splice(i, 1);
30337                         return;
30338                     }
30339                 }
30340             }else{
30341                  i = accessList.indexOf(dlg);
30342                 if(i != -1){
30343                     accessList.splice(i, 1);
30344                 }
30345             }
30346         },
30347
30348         /**
30349          * Gets a registered dialog by id
30350          * @param {String/Object} id The id of the dialog or a dialog
30351          * @return {Roo.BasicDialog} this
30352          */
30353         get : function(id){
30354             return typeof id == "object" ? id : list[id];
30355         },
30356
30357         /**
30358          * Brings the specified dialog to the front
30359          * @param {String/Object} dlg The id of the dialog or a dialog
30360          * @return {Roo.BasicDialog} this
30361          */
30362         bringToFront : function(dlg){
30363             dlg = this.get(dlg);
30364             if(dlg != front){
30365                 front = dlg;
30366                 dlg._lastAccess = new Date().getTime();
30367                 orderDialogs();
30368             }
30369             return dlg;
30370         },
30371
30372         /**
30373          * Sends the specified dialog to the back
30374          * @param {String/Object} dlg The id of the dialog or a dialog
30375          * @return {Roo.BasicDialog} this
30376          */
30377         sendToBack : function(dlg){
30378             dlg = this.get(dlg);
30379             dlg._lastAccess = -(new Date().getTime());
30380             orderDialogs();
30381             return dlg;
30382         },
30383
30384         /**
30385          * Hides all dialogs
30386          */
30387         hideAll : function(){
30388             for(var id in list){
30389                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
30390                     list[id].hide();
30391                 }
30392             }
30393         }
30394     };
30395 }();
30396
30397 /**
30398  * @class Roo.LayoutDialog
30399  * @extends Roo.BasicDialog
30400  * Dialog which provides adjustments for working with a layout in a Dialog.
30401  * Add your necessary layout config options to the dialog's config.<br>
30402  * Example usage (including a nested layout):
30403  * <pre><code>
30404 if(!dialog){
30405     dialog = new Roo.LayoutDialog("download-dlg", {
30406         modal: true,
30407         width:600,
30408         height:450,
30409         shadow:true,
30410         minWidth:500,
30411         minHeight:350,
30412         autoTabs:true,
30413         proxyDrag:true,
30414         // layout config merges with the dialog config
30415         center:{
30416             tabPosition: "top",
30417             alwaysShowTabs: true
30418         }
30419     });
30420     dialog.addKeyListener(27, dialog.hide, dialog);
30421     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
30422     dialog.addButton("Build It!", this.getDownload, this);
30423
30424     // we can even add nested layouts
30425     var innerLayout = new Roo.BorderLayout("dl-inner", {
30426         east: {
30427             initialSize: 200,
30428             autoScroll:true,
30429             split:true
30430         },
30431         center: {
30432             autoScroll:true
30433         }
30434     });
30435     innerLayout.beginUpdate();
30436     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
30437     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
30438     innerLayout.endUpdate(true);
30439
30440     var layout = dialog.getLayout();
30441     layout.beginUpdate();
30442     layout.add("center", new Roo.ContentPanel("standard-panel",
30443                         {title: "Download the Source", fitToFrame:true}));
30444     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
30445                {title: "Build your own roo.js"}));
30446     layout.getRegion("center").showPanel(sp);
30447     layout.endUpdate();
30448 }
30449 </code></pre>
30450     * @constructor
30451     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
30452     * @param {Object} config configuration options
30453   */
30454 Roo.LayoutDialog = function(el, cfg){
30455     
30456     var config=  cfg;
30457     if (typeof(cfg) == 'undefined') {
30458         config = Roo.apply({}, el);
30459         // not sure why we use documentElement here.. - it should always be body.
30460         // IE7 borks horribly if we use documentElement.
30461         // webkit also does not like documentElement - it creates a body element...
30462         el = Roo.get( document.body || document.documentElement ).createChild();
30463         //config.autoCreate = true;
30464     }
30465     
30466     
30467     config.autoTabs = false;
30468     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
30469     this.body.setStyle({overflow:"hidden", position:"relative"});
30470     this.layout = new Roo.BorderLayout(this.body.dom, config);
30471     this.layout.monitorWindowResize = false;
30472     this.el.addClass("x-dlg-auto-layout");
30473     // fix case when center region overwrites center function
30474     this.center = Roo.BasicDialog.prototype.center;
30475     this.on("show", this.layout.layout, this.layout, true);
30476     if (config.items) {
30477         var xitems = config.items;
30478         delete config.items;
30479         Roo.each(xitems, this.addxtype, this);
30480     }
30481     
30482     
30483 };
30484 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
30485     /**
30486      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
30487      * @deprecated
30488      */
30489     endUpdate : function(){
30490         this.layout.endUpdate();
30491     },
30492
30493     /**
30494      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
30495      *  @deprecated
30496      */
30497     beginUpdate : function(){
30498         this.layout.beginUpdate();
30499     },
30500
30501     /**
30502      * Get the BorderLayout for this dialog
30503      * @return {Roo.BorderLayout}
30504      */
30505     getLayout : function(){
30506         return this.layout;
30507     },
30508
30509     showEl : function(){
30510         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
30511         if(Roo.isIE7){
30512             this.layout.layout();
30513         }
30514     },
30515
30516     // private
30517     // Use the syncHeightBeforeShow config option to control this automatically
30518     syncBodyHeight : function(){
30519         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
30520         if(this.layout){this.layout.layout();}
30521     },
30522     
30523       /**
30524      * Add an xtype element (actually adds to the layout.)
30525      * @return {Object} xdata xtype object data.
30526      */
30527     
30528     addxtype : function(c) {
30529         return this.layout.addxtype(c);
30530     }
30531 });/*
30532  * Based on:
30533  * Ext JS Library 1.1.1
30534  * Copyright(c) 2006-2007, Ext JS, LLC.
30535  *
30536  * Originally Released Under LGPL - original licence link has changed is not relivant.
30537  *
30538  * Fork - LGPL
30539  * <script type="text/javascript">
30540  */
30541  
30542 /**
30543  * @class Roo.MessageBox
30544  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
30545  * Example usage:
30546  *<pre><code>
30547 // Basic alert:
30548 Roo.Msg.alert('Status', 'Changes saved successfully.');
30549
30550 // Prompt for user data:
30551 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
30552     if (btn == 'ok'){
30553         // process text value...
30554     }
30555 });
30556
30557 // Show a dialog using config options:
30558 Roo.Msg.show({
30559    title:'Save Changes?',
30560    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
30561    buttons: Roo.Msg.YESNOCANCEL,
30562    fn: processResult,
30563    animEl: 'elId'
30564 });
30565 </code></pre>
30566  * @singleton
30567  */
30568 Roo.MessageBox = function(){
30569     var dlg, opt, mask, waitTimer;
30570     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30571     var buttons, activeTextEl, bwidth;
30572
30573     // private
30574     var handleButton = function(button){
30575         dlg.hide();
30576         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30577     };
30578
30579     // private
30580     var handleHide = function(){
30581         if(opt && opt.cls){
30582             dlg.el.removeClass(opt.cls);
30583         }
30584         if(waitTimer){
30585             Roo.TaskMgr.stop(waitTimer);
30586             waitTimer = null;
30587         }
30588     };
30589
30590     // private
30591     var updateButtons = function(b){
30592         var width = 0;
30593         if(!b){
30594             buttons["ok"].hide();
30595             buttons["cancel"].hide();
30596             buttons["yes"].hide();
30597             buttons["no"].hide();
30598             dlg.footer.dom.style.display = 'none';
30599             return width;
30600         }
30601         dlg.footer.dom.style.display = '';
30602         for(var k in buttons){
30603             if(typeof buttons[k] != "function"){
30604                 if(b[k]){
30605                     buttons[k].show();
30606                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30607                     width += buttons[k].el.getWidth()+15;
30608                 }else{
30609                     buttons[k].hide();
30610                 }
30611             }
30612         }
30613         return width;
30614     };
30615
30616     // private
30617     var handleEsc = function(d, k, e){
30618         if(opt && opt.closable !== false){
30619             dlg.hide();
30620         }
30621         if(e){
30622             e.stopEvent();
30623         }
30624     };
30625
30626     return {
30627         /**
30628          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30629          * @return {Roo.BasicDialog} The BasicDialog element
30630          */
30631         getDialog : function(){
30632            if(!dlg){
30633                 dlg = new Roo.BasicDialog("x-msg-box", {
30634                     autoCreate : true,
30635                     shadow: true,
30636                     draggable: true,
30637                     resizable:false,
30638                     constraintoviewport:false,
30639                     fixedcenter:true,
30640                     collapsible : false,
30641                     shim:true,
30642                     modal: true,
30643                     width:400, height:100,
30644                     buttonAlign:"center",
30645                     closeClick : function(){
30646                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30647                             handleButton("no");
30648                         }else{
30649                             handleButton("cancel");
30650                         }
30651                     }
30652                 });
30653                 dlg.on("hide", handleHide);
30654                 mask = dlg.mask;
30655                 dlg.addKeyListener(27, handleEsc);
30656                 buttons = {};
30657                 var bt = this.buttonText;
30658                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30659                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30660                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30661                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30662                 bodyEl = dlg.body.createChild({
30663
30664                     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>'
30665                 });
30666                 msgEl = bodyEl.dom.firstChild;
30667                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30668                 textboxEl.enableDisplayMode();
30669                 textboxEl.addKeyListener([10,13], function(){
30670                     if(dlg.isVisible() && opt && opt.buttons){
30671                         if(opt.buttons.ok){
30672                             handleButton("ok");
30673                         }else if(opt.buttons.yes){
30674                             handleButton("yes");
30675                         }
30676                     }
30677                 });
30678                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30679                 textareaEl.enableDisplayMode();
30680                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30681                 progressEl.enableDisplayMode();
30682                 var pf = progressEl.dom.firstChild;
30683                 if (pf) {
30684                     pp = Roo.get(pf.firstChild);
30685                     pp.setHeight(pf.offsetHeight);
30686                 }
30687                 
30688             }
30689             return dlg;
30690         },
30691
30692         /**
30693          * Updates the message box body text
30694          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30695          * the XHTML-compliant non-breaking space character '&amp;#160;')
30696          * @return {Roo.MessageBox} This message box
30697          */
30698         updateText : function(text){
30699             if(!dlg.isVisible() && !opt.width){
30700                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30701             }
30702             msgEl.innerHTML = text || '&#160;';
30703       
30704             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30705             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30706             var w = Math.max(
30707                     Math.min(opt.width || cw , this.maxWidth), 
30708                     Math.max(opt.minWidth || this.minWidth, bwidth)
30709             );
30710             if(opt.prompt){
30711                 activeTextEl.setWidth(w);
30712             }
30713             if(dlg.isVisible()){
30714                 dlg.fixedcenter = false;
30715             }
30716             // to big, make it scroll. = But as usual stupid IE does not support
30717             // !important..
30718             
30719             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30720                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30721                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30722             } else {
30723                 bodyEl.dom.style.height = '';
30724                 bodyEl.dom.style.overflowY = '';
30725             }
30726             if (cw > w) {
30727                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30728             } else {
30729                 bodyEl.dom.style.overflowX = '';
30730             }
30731             
30732             dlg.setContentSize(w, bodyEl.getHeight());
30733             if(dlg.isVisible()){
30734                 dlg.fixedcenter = true;
30735             }
30736             return this;
30737         },
30738
30739         /**
30740          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30741          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30742          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30743          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30744          * @return {Roo.MessageBox} This message box
30745          */
30746         updateProgress : function(value, text){
30747             if(text){
30748                 this.updateText(text);
30749             }
30750             if (pp) { // weird bug on my firefox - for some reason this is not defined
30751                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30752             }
30753             return this;
30754         },        
30755
30756         /**
30757          * Returns true if the message box is currently displayed
30758          * @return {Boolean} True if the message box is visible, else false
30759          */
30760         isVisible : function(){
30761             return dlg && dlg.isVisible();  
30762         },
30763
30764         /**
30765          * Hides the message box if it is displayed
30766          */
30767         hide : function(){
30768             if(this.isVisible()){
30769                 dlg.hide();
30770             }  
30771         },
30772
30773         /**
30774          * Displays a new message box, or reinitializes an existing message box, based on the config options
30775          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30776          * The following config object properties are supported:
30777          * <pre>
30778 Property    Type             Description
30779 ----------  ---------------  ------------------------------------------------------------------------------------
30780 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30781                                    closes (defaults to undefined)
30782 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30783                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30784 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30785                                    progress and wait dialogs will ignore this property and always hide the
30786                                    close button as they can only be closed programmatically.
30787 cls               String           A custom CSS class to apply to the message box element
30788 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30789                                    displayed (defaults to 75)
30790 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30791                                    function will be btn (the name of the button that was clicked, if applicable,
30792                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30793                                    Progress and wait dialogs will ignore this option since they do not respond to
30794                                    user actions and can only be closed programmatically, so any required function
30795                                    should be called by the same code after it closes the dialog.
30796 icon              String           A CSS class that provides a background image to be used as an icon for
30797                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30798 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30799 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30800 modal             Boolean          False to allow user interaction with the page while the message box is
30801                                    displayed (defaults to true)
30802 msg               String           A string that will replace the existing message box body text (defaults
30803                                    to the XHTML-compliant non-breaking space character '&#160;')
30804 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30805 progress          Boolean          True to display a progress bar (defaults to false)
30806 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30807 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30808 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30809 title             String           The title text
30810 value             String           The string value to set into the active textbox element if displayed
30811 wait              Boolean          True to display a progress bar (defaults to false)
30812 width             Number           The width of the dialog in pixels
30813 </pre>
30814          *
30815          * Example usage:
30816          * <pre><code>
30817 Roo.Msg.show({
30818    title: 'Address',
30819    msg: 'Please enter your address:',
30820    width: 300,
30821    buttons: Roo.MessageBox.OKCANCEL,
30822    multiline: true,
30823    fn: saveAddress,
30824    animEl: 'addAddressBtn'
30825 });
30826 </code></pre>
30827          * @param {Object} config Configuration options
30828          * @return {Roo.MessageBox} This message box
30829          */
30830         show : function(options)
30831         {
30832             
30833             // this causes nightmares if you show one dialog after another
30834             // especially on callbacks..
30835              
30836             if(this.isVisible()){
30837                 
30838                 this.hide();
30839                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30840                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30841                 Roo.log("New Dialog Message:" +  options.msg )
30842                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30843                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30844                 
30845             }
30846             var d = this.getDialog();
30847             opt = options;
30848             d.setTitle(opt.title || "&#160;");
30849             d.close.setDisplayed(opt.closable !== false);
30850             activeTextEl = textboxEl;
30851             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30852             if(opt.prompt){
30853                 if(opt.multiline){
30854                     textboxEl.hide();
30855                     textareaEl.show();
30856                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30857                         opt.multiline : this.defaultTextHeight);
30858                     activeTextEl = textareaEl;
30859                 }else{
30860                     textboxEl.show();
30861                     textareaEl.hide();
30862                 }
30863             }else{
30864                 textboxEl.hide();
30865                 textareaEl.hide();
30866             }
30867             progressEl.setDisplayed(opt.progress === true);
30868             this.updateProgress(0);
30869             activeTextEl.dom.value = opt.value || "";
30870             if(opt.prompt){
30871                 dlg.setDefaultButton(activeTextEl);
30872             }else{
30873                 var bs = opt.buttons;
30874                 var db = null;
30875                 if(bs && bs.ok){
30876                     db = buttons["ok"];
30877                 }else if(bs && bs.yes){
30878                     db = buttons["yes"];
30879                 }
30880                 dlg.setDefaultButton(db);
30881             }
30882             bwidth = updateButtons(opt.buttons);
30883             this.updateText(opt.msg);
30884             if(opt.cls){
30885                 d.el.addClass(opt.cls);
30886             }
30887             d.proxyDrag = opt.proxyDrag === true;
30888             d.modal = opt.modal !== false;
30889             d.mask = opt.modal !== false ? mask : false;
30890             if(!d.isVisible()){
30891                 // force it to the end of the z-index stack so it gets a cursor in FF
30892                 document.body.appendChild(dlg.el.dom);
30893                 d.animateTarget = null;
30894                 d.show(options.animEl);
30895             }
30896             return this;
30897         },
30898
30899         /**
30900          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30901          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30902          * and closing the message box when the process is complete.
30903          * @param {String} title The title bar text
30904          * @param {String} msg The message box body text
30905          * @return {Roo.MessageBox} This message box
30906          */
30907         progress : function(title, msg){
30908             this.show({
30909                 title : title,
30910                 msg : msg,
30911                 buttons: false,
30912                 progress:true,
30913                 closable:false,
30914                 minWidth: this.minProgressWidth,
30915                 modal : true
30916             });
30917             return this;
30918         },
30919
30920         /**
30921          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30922          * If a callback function is passed it will be called after the user clicks the button, and the
30923          * id of the button that was clicked will be passed as the only parameter to the callback
30924          * (could also be the top-right close button).
30925          * @param {String} title The title bar text
30926          * @param {String} msg The message box body text
30927          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30928          * @param {Object} scope (optional) The scope of the callback function
30929          * @return {Roo.MessageBox} This message box
30930          */
30931         alert : function(title, msg, fn, scope){
30932             this.show({
30933                 title : title,
30934                 msg : msg,
30935                 buttons: this.OK,
30936                 fn: fn,
30937                 scope : scope,
30938                 modal : true
30939             });
30940             return this;
30941         },
30942
30943         /**
30944          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30945          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30946          * You are responsible for closing the message box when the process is complete.
30947          * @param {String} msg The message box body text
30948          * @param {String} title (optional) The title bar text
30949          * @return {Roo.MessageBox} This message box
30950          */
30951         wait : function(msg, title){
30952             this.show({
30953                 title : title,
30954                 msg : msg,
30955                 buttons: false,
30956                 closable:false,
30957                 progress:true,
30958                 modal:true,
30959                 width:300,
30960                 wait:true
30961             });
30962             waitTimer = Roo.TaskMgr.start({
30963                 run: function(i){
30964                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30965                 },
30966                 interval: 1000
30967             });
30968             return this;
30969         },
30970
30971         /**
30972          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30973          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30974          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30975          * @param {String} title The title bar text
30976          * @param {String} msg The message box body text
30977          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30978          * @param {Object} scope (optional) The scope of the callback function
30979          * @return {Roo.MessageBox} This message box
30980          */
30981         confirm : function(title, msg, fn, scope){
30982             this.show({
30983                 title : title,
30984                 msg : msg,
30985                 buttons: this.YESNO,
30986                 fn: fn,
30987                 scope : scope,
30988                 modal : true
30989             });
30990             return this;
30991         },
30992
30993         /**
30994          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30995          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30996          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30997          * (could also be the top-right close button) and the text that was entered will be passed as the two
30998          * parameters to the callback.
30999          * @param {String} title The title bar text
31000          * @param {String} msg The message box body text
31001          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31002          * @param {Object} scope (optional) The scope of the callback function
31003          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31004          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31005          * @return {Roo.MessageBox} This message box
31006          */
31007         prompt : function(title, msg, fn, scope, multiline){
31008             this.show({
31009                 title : title,
31010                 msg : msg,
31011                 buttons: this.OKCANCEL,
31012                 fn: fn,
31013                 minWidth:250,
31014                 scope : scope,
31015                 prompt:true,
31016                 multiline: multiline,
31017                 modal : true
31018             });
31019             return this;
31020         },
31021
31022         /**
31023          * Button config that displays a single OK button
31024          * @type Object
31025          */
31026         OK : {ok:true},
31027         /**
31028          * Button config that displays Yes and No buttons
31029          * @type Object
31030          */
31031         YESNO : {yes:true, no:true},
31032         /**
31033          * Button config that displays OK and Cancel buttons
31034          * @type Object
31035          */
31036         OKCANCEL : {ok:true, cancel:true},
31037         /**
31038          * Button config that displays Yes, No and Cancel buttons
31039          * @type Object
31040          */
31041         YESNOCANCEL : {yes:true, no:true, cancel:true},
31042
31043         /**
31044          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31045          * @type Number
31046          */
31047         defaultTextHeight : 75,
31048         /**
31049          * The maximum width in pixels of the message box (defaults to 600)
31050          * @type Number
31051          */
31052         maxWidth : 600,
31053         /**
31054          * The minimum width in pixels of the message box (defaults to 100)
31055          * @type Number
31056          */
31057         minWidth : 100,
31058         /**
31059          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31060          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31061          * @type Number
31062          */
31063         minProgressWidth : 250,
31064         /**
31065          * An object containing the default button text strings that can be overriden for localized language support.
31066          * Supported properties are: ok, cancel, yes and no.
31067          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31068          * @type Object
31069          */
31070         buttonText : {
31071             ok : "OK",
31072             cancel : "Cancel",
31073             yes : "Yes",
31074             no : "No"
31075         }
31076     };
31077 }();
31078
31079 /**
31080  * Shorthand for {@link Roo.MessageBox}
31081  */
31082 Roo.Msg = Roo.MessageBox;/*
31083  * Based on:
31084  * Ext JS Library 1.1.1
31085  * Copyright(c) 2006-2007, Ext JS, LLC.
31086  *
31087  * Originally Released Under LGPL - original licence link has changed is not relivant.
31088  *
31089  * Fork - LGPL
31090  * <script type="text/javascript">
31091  */
31092 /**
31093  * @class Roo.QuickTips
31094  * Provides attractive and customizable tooltips for any element.
31095  * @singleton
31096  */
31097 Roo.QuickTips = function(){
31098     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31099     var ce, bd, xy, dd;
31100     var visible = false, disabled = true, inited = false;
31101     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31102     
31103     var onOver = function(e){
31104         if(disabled){
31105             return;
31106         }
31107         var t = e.getTarget();
31108         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31109             return;
31110         }
31111         if(ce && t == ce.el){
31112             clearTimeout(hideProc);
31113             return;
31114         }
31115         if(t && tagEls[t.id]){
31116             tagEls[t.id].el = t;
31117             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31118             return;
31119         }
31120         var ttp, et = Roo.fly(t);
31121         var ns = cfg.namespace;
31122         if(tm.interceptTitles && t.title){
31123             ttp = t.title;
31124             t.qtip = ttp;
31125             t.removeAttribute("title");
31126             e.preventDefault();
31127         }else{
31128             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31129         }
31130         if(ttp){
31131             showProc = show.defer(tm.showDelay, tm, [{
31132                 el: t, 
31133                 text: ttp, 
31134                 width: et.getAttributeNS(ns, cfg.width),
31135                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31136                 title: et.getAttributeNS(ns, cfg.title),
31137                     cls: et.getAttributeNS(ns, cfg.cls)
31138             }]);
31139         }
31140     };
31141     
31142     var onOut = function(e){
31143         clearTimeout(showProc);
31144         var t = e.getTarget();
31145         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31146             hideProc = setTimeout(hide, tm.hideDelay);
31147         }
31148     };
31149     
31150     var onMove = function(e){
31151         if(disabled){
31152             return;
31153         }
31154         xy = e.getXY();
31155         xy[1] += 18;
31156         if(tm.trackMouse && ce){
31157             el.setXY(xy);
31158         }
31159     };
31160     
31161     var onDown = function(e){
31162         clearTimeout(showProc);
31163         clearTimeout(hideProc);
31164         if(!e.within(el)){
31165             if(tm.hideOnClick){
31166                 hide();
31167                 tm.disable();
31168                 tm.enable.defer(100, tm);
31169             }
31170         }
31171     };
31172     
31173     var getPad = function(){
31174         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31175     };
31176
31177     var show = function(o){
31178         if(disabled){
31179             return;
31180         }
31181         clearTimeout(dismissProc);
31182         ce = o;
31183         if(removeCls){ // in case manually hidden
31184             el.removeClass(removeCls);
31185             removeCls = null;
31186         }
31187         if(ce.cls){
31188             el.addClass(ce.cls);
31189             removeCls = ce.cls;
31190         }
31191         if(ce.title){
31192             tipTitle.update(ce.title);
31193             tipTitle.show();
31194         }else{
31195             tipTitle.update('');
31196             tipTitle.hide();
31197         }
31198         el.dom.style.width  = tm.maxWidth+'px';
31199         //tipBody.dom.style.width = '';
31200         tipBodyText.update(o.text);
31201         var p = getPad(), w = ce.width;
31202         if(!w){
31203             var td = tipBodyText.dom;
31204             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31205             if(aw > tm.maxWidth){
31206                 w = tm.maxWidth;
31207             }else if(aw < tm.minWidth){
31208                 w = tm.minWidth;
31209             }else{
31210                 w = aw;
31211             }
31212         }
31213         //tipBody.setWidth(w);
31214         el.setWidth(parseInt(w, 10) + p);
31215         if(ce.autoHide === false){
31216             close.setDisplayed(true);
31217             if(dd){
31218                 dd.unlock();
31219             }
31220         }else{
31221             close.setDisplayed(false);
31222             if(dd){
31223                 dd.lock();
31224             }
31225         }
31226         if(xy){
31227             el.avoidY = xy[1]-18;
31228             el.setXY(xy);
31229         }
31230         if(tm.animate){
31231             el.setOpacity(.1);
31232             el.setStyle("visibility", "visible");
31233             el.fadeIn({callback: afterShow});
31234         }else{
31235             afterShow();
31236         }
31237     };
31238     
31239     var afterShow = function(){
31240         if(ce){
31241             el.show();
31242             esc.enable();
31243             if(tm.autoDismiss && ce.autoHide !== false){
31244                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31245             }
31246         }
31247     };
31248     
31249     var hide = function(noanim){
31250         clearTimeout(dismissProc);
31251         clearTimeout(hideProc);
31252         ce = null;
31253         if(el.isVisible()){
31254             esc.disable();
31255             if(noanim !== true && tm.animate){
31256                 el.fadeOut({callback: afterHide});
31257             }else{
31258                 afterHide();
31259             } 
31260         }
31261     };
31262     
31263     var afterHide = function(){
31264         el.hide();
31265         if(removeCls){
31266             el.removeClass(removeCls);
31267             removeCls = null;
31268         }
31269     };
31270     
31271     return {
31272         /**
31273         * @cfg {Number} minWidth
31274         * The minimum width of the quick tip (defaults to 40)
31275         */
31276        minWidth : 40,
31277         /**
31278         * @cfg {Number} maxWidth
31279         * The maximum width of the quick tip (defaults to 300)
31280         */
31281        maxWidth : 300,
31282         /**
31283         * @cfg {Boolean} interceptTitles
31284         * True to automatically use the element's DOM title value if available (defaults to false)
31285         */
31286        interceptTitles : false,
31287         /**
31288         * @cfg {Boolean} trackMouse
31289         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31290         */
31291        trackMouse : false,
31292         /**
31293         * @cfg {Boolean} hideOnClick
31294         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31295         */
31296        hideOnClick : true,
31297         /**
31298         * @cfg {Number} showDelay
31299         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31300         */
31301        showDelay : 500,
31302         /**
31303         * @cfg {Number} hideDelay
31304         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31305         */
31306        hideDelay : 200,
31307         /**
31308         * @cfg {Boolean} autoHide
31309         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31310         * Used in conjunction with hideDelay.
31311         */
31312        autoHide : true,
31313         /**
31314         * @cfg {Boolean}
31315         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31316         * (defaults to true).  Used in conjunction with autoDismissDelay.
31317         */
31318        autoDismiss : true,
31319         /**
31320         * @cfg {Number}
31321         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31322         */
31323        autoDismissDelay : 5000,
31324        /**
31325         * @cfg {Boolean} animate
31326         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31327         */
31328        animate : false,
31329
31330        /**
31331         * @cfg {String} title
31332         * Title text to display (defaults to '').  This can be any valid HTML markup.
31333         */
31334         title: '',
31335        /**
31336         * @cfg {String} text
31337         * Body text to display (defaults to '').  This can be any valid HTML markup.
31338         */
31339         text : '',
31340        /**
31341         * @cfg {String} cls
31342         * A CSS class to apply to the base quick tip element (defaults to '').
31343         */
31344         cls : '',
31345        /**
31346         * @cfg {Number} width
31347         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
31348         * minWidth or maxWidth.
31349         */
31350         width : null,
31351
31352     /**
31353      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
31354      * or display QuickTips in a page.
31355      */
31356        init : function(){
31357           tm = Roo.QuickTips;
31358           cfg = tm.tagConfig;
31359           if(!inited){
31360               if(!Roo.isReady){ // allow calling of init() before onReady
31361                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
31362                   return;
31363               }
31364               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
31365               el.fxDefaults = {stopFx: true};
31366               // maximum custom styling
31367               //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>');
31368               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>');              
31369               tipTitle = el.child('h3');
31370               tipTitle.enableDisplayMode("block");
31371               tipBody = el.child('div.x-tip-bd');
31372               tipBodyText = el.child('div.x-tip-bd-inner');
31373               //bdLeft = el.child('div.x-tip-bd-left');
31374               //bdRight = el.child('div.x-tip-bd-right');
31375               close = el.child('div.x-tip-close');
31376               close.enableDisplayMode("block");
31377               close.on("click", hide);
31378               var d = Roo.get(document);
31379               d.on("mousedown", onDown);
31380               d.on("mouseover", onOver);
31381               d.on("mouseout", onOut);
31382               d.on("mousemove", onMove);
31383               esc = d.addKeyListener(27, hide);
31384               esc.disable();
31385               if(Roo.dd.DD){
31386                   dd = el.initDD("default", null, {
31387                       onDrag : function(){
31388                           el.sync();  
31389                       }
31390                   });
31391                   dd.setHandleElId(tipTitle.id);
31392                   dd.lock();
31393               }
31394               inited = true;
31395           }
31396           this.enable(); 
31397        },
31398
31399     /**
31400      * Configures a new quick tip instance and assigns it to a target element.  The following config options
31401      * are supported:
31402      * <pre>
31403 Property    Type                   Description
31404 ----------  ---------------------  ------------------------------------------------------------------------
31405 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
31406      * </ul>
31407      * @param {Object} config The config object
31408      */
31409        register : function(config){
31410            var cs = config instanceof Array ? config : arguments;
31411            for(var i = 0, len = cs.length; i < len; i++) {
31412                var c = cs[i];
31413                var target = c.target;
31414                if(target){
31415                    if(target instanceof Array){
31416                        for(var j = 0, jlen = target.length; j < jlen; j++){
31417                            tagEls[target[j]] = c;
31418                        }
31419                    }else{
31420                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
31421                    }
31422                }
31423            }
31424        },
31425
31426     /**
31427      * Removes this quick tip from its element and destroys it.
31428      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
31429      */
31430        unregister : function(el){
31431            delete tagEls[Roo.id(el)];
31432        },
31433
31434     /**
31435      * Enable this quick tip.
31436      */
31437        enable : function(){
31438            if(inited && disabled){
31439                locks.pop();
31440                if(locks.length < 1){
31441                    disabled = false;
31442                }
31443            }
31444        },
31445
31446     /**
31447      * Disable this quick tip.
31448      */
31449        disable : function(){
31450           disabled = true;
31451           clearTimeout(showProc);
31452           clearTimeout(hideProc);
31453           clearTimeout(dismissProc);
31454           if(ce){
31455               hide(true);
31456           }
31457           locks.push(1);
31458        },
31459
31460     /**
31461      * Returns true if the quick tip is enabled, else false.
31462      */
31463        isEnabled : function(){
31464             return !disabled;
31465        },
31466
31467         // private
31468        tagConfig : {
31469            namespace : "ext",
31470            attribute : "qtip",
31471            width : "width",
31472            target : "target",
31473            title : "qtitle",
31474            hide : "hide",
31475            cls : "qclass"
31476        }
31477    };
31478 }();
31479
31480 // backwards compat
31481 Roo.QuickTips.tips = Roo.QuickTips.register;/*
31482  * Based on:
31483  * Ext JS Library 1.1.1
31484  * Copyright(c) 2006-2007, Ext JS, LLC.
31485  *
31486  * Originally Released Under LGPL - original licence link has changed is not relivant.
31487  *
31488  * Fork - LGPL
31489  * <script type="text/javascript">
31490  */
31491  
31492
31493 /**
31494  * @class Roo.tree.TreePanel
31495  * @extends Roo.data.Tree
31496
31497  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
31498  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
31499  * @cfg {Boolean} enableDD true to enable drag and drop
31500  * @cfg {Boolean} enableDrag true to enable just drag
31501  * @cfg {Boolean} enableDrop true to enable just drop
31502  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
31503  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
31504  * @cfg {String} ddGroup The DD group this TreePanel belongs to
31505  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
31506  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
31507  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
31508  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
31509  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
31510  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
31511  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
31512  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
31513  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
31514  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
31515  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
31516  * @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>
31517  * @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>
31518  * 
31519  * @constructor
31520  * @param {String/HTMLElement/Element} el The container element
31521  * @param {Object} config
31522  */
31523 Roo.tree.TreePanel = function(el, config){
31524     var root = false;
31525     var loader = false;
31526     if (config.root) {
31527         root = config.root;
31528         delete config.root;
31529     }
31530     if (config.loader) {
31531         loader = config.loader;
31532         delete config.loader;
31533     }
31534     
31535     Roo.apply(this, config);
31536     Roo.tree.TreePanel.superclass.constructor.call(this);
31537     this.el = Roo.get(el);
31538     this.el.addClass('x-tree');
31539     //console.log(root);
31540     if (root) {
31541         this.setRootNode( Roo.factory(root, Roo.tree));
31542     }
31543     if (loader) {
31544         this.loader = Roo.factory(loader, Roo.tree);
31545     }
31546    /**
31547     * Read-only. The id of the container element becomes this TreePanel's id.
31548     */
31549     this.id = this.el.id;
31550     this.addEvents({
31551         /**
31552         * @event beforeload
31553         * Fires before a node is loaded, return false to cancel
31554         * @param {Node} node The node being loaded
31555         */
31556         "beforeload" : true,
31557         /**
31558         * @event load
31559         * Fires when a node is loaded
31560         * @param {Node} node The node that was loaded
31561         */
31562         "load" : true,
31563         /**
31564         * @event textchange
31565         * Fires when the text for a node is changed
31566         * @param {Node} node The node
31567         * @param {String} text The new text
31568         * @param {String} oldText The old text
31569         */
31570         "textchange" : true,
31571         /**
31572         * @event beforeexpand
31573         * Fires before a node is expanded, return false to cancel.
31574         * @param {Node} node The node
31575         * @param {Boolean} deep
31576         * @param {Boolean} anim
31577         */
31578         "beforeexpand" : true,
31579         /**
31580         * @event beforecollapse
31581         * Fires before a node is collapsed, return false to cancel.
31582         * @param {Node} node The node
31583         * @param {Boolean} deep
31584         * @param {Boolean} anim
31585         */
31586         "beforecollapse" : true,
31587         /**
31588         * @event expand
31589         * Fires when a node is expanded
31590         * @param {Node} node The node
31591         */
31592         "expand" : true,
31593         /**
31594         * @event disabledchange
31595         * Fires when the disabled status of a node changes
31596         * @param {Node} node The node
31597         * @param {Boolean} disabled
31598         */
31599         "disabledchange" : true,
31600         /**
31601         * @event collapse
31602         * Fires when a node is collapsed
31603         * @param {Node} node The node
31604         */
31605         "collapse" : true,
31606         /**
31607         * @event beforeclick
31608         * Fires before click processing on a node. Return false to cancel the default action.
31609         * @param {Node} node The node
31610         * @param {Roo.EventObject} e The event object
31611         */
31612         "beforeclick":true,
31613         /**
31614         * @event checkchange
31615         * Fires when a node with a checkbox's checked property changes
31616         * @param {Node} this This node
31617         * @param {Boolean} checked
31618         */
31619         "checkchange":true,
31620         /**
31621         * @event click
31622         * Fires when a node is clicked
31623         * @param {Node} node The node
31624         * @param {Roo.EventObject} e The event object
31625         */
31626         "click":true,
31627         /**
31628         * @event dblclick
31629         * Fires when a node is double clicked
31630         * @param {Node} node The node
31631         * @param {Roo.EventObject} e The event object
31632         */
31633         "dblclick":true,
31634         /**
31635         * @event contextmenu
31636         * Fires when a node is right clicked
31637         * @param {Node} node The node
31638         * @param {Roo.EventObject} e The event object
31639         */
31640         "contextmenu":true,
31641         /**
31642         * @event beforechildrenrendered
31643         * Fires right before the child nodes for a node are rendered
31644         * @param {Node} node The node
31645         */
31646         "beforechildrenrendered":true,
31647         /**
31648         * @event startdrag
31649         * Fires when a node starts being dragged
31650         * @param {Roo.tree.TreePanel} this
31651         * @param {Roo.tree.TreeNode} node
31652         * @param {event} e The raw browser event
31653         */ 
31654        "startdrag" : true,
31655        /**
31656         * @event enddrag
31657         * Fires when a drag operation is complete
31658         * @param {Roo.tree.TreePanel} this
31659         * @param {Roo.tree.TreeNode} node
31660         * @param {event} e The raw browser event
31661         */
31662        "enddrag" : true,
31663        /**
31664         * @event dragdrop
31665         * Fires when a dragged node is dropped on a valid DD target
31666         * @param {Roo.tree.TreePanel} this
31667         * @param {Roo.tree.TreeNode} node
31668         * @param {DD} dd The dd it was dropped on
31669         * @param {event} e The raw browser event
31670         */
31671        "dragdrop" : true,
31672        /**
31673         * @event beforenodedrop
31674         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31675         * passed to handlers has the following properties:<br />
31676         * <ul style="padding:5px;padding-left:16px;">
31677         * <li>tree - The TreePanel</li>
31678         * <li>target - The node being targeted for the drop</li>
31679         * <li>data - The drag data from the drag source</li>
31680         * <li>point - The point of the drop - append, above or below</li>
31681         * <li>source - The drag source</li>
31682         * <li>rawEvent - Raw mouse event</li>
31683         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31684         * to be inserted by setting them on this object.</li>
31685         * <li>cancel - Set this to true to cancel the drop.</li>
31686         * </ul>
31687         * @param {Object} dropEvent
31688         */
31689        "beforenodedrop" : true,
31690        /**
31691         * @event nodedrop
31692         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31693         * passed to handlers has the following properties:<br />
31694         * <ul style="padding:5px;padding-left:16px;">
31695         * <li>tree - The TreePanel</li>
31696         * <li>target - The node being targeted for the drop</li>
31697         * <li>data - The drag data from the drag source</li>
31698         * <li>point - The point of the drop - append, above or below</li>
31699         * <li>source - The drag source</li>
31700         * <li>rawEvent - Raw mouse event</li>
31701         * <li>dropNode - Dropped node(s).</li>
31702         * </ul>
31703         * @param {Object} dropEvent
31704         */
31705        "nodedrop" : true,
31706         /**
31707         * @event nodedragover
31708         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31709         * passed to handlers has the following properties:<br />
31710         * <ul style="padding:5px;padding-left:16px;">
31711         * <li>tree - The TreePanel</li>
31712         * <li>target - The node being targeted for the drop</li>
31713         * <li>data - The drag data from the drag source</li>
31714         * <li>point - The point of the drop - append, above or below</li>
31715         * <li>source - The drag source</li>
31716         * <li>rawEvent - Raw mouse event</li>
31717         * <li>dropNode - Drop node(s) provided by the source.</li>
31718         * <li>cancel - Set this to true to signal drop not allowed.</li>
31719         * </ul>
31720         * @param {Object} dragOverEvent
31721         */
31722        "nodedragover" : true
31723         
31724     });
31725     if(this.singleExpand){
31726        this.on("beforeexpand", this.restrictExpand, this);
31727     }
31728     if (this.editor) {
31729         this.editor.tree = this;
31730         this.editor = Roo.factory(this.editor, Roo.tree);
31731     }
31732     
31733     if (this.selModel) {
31734         this.selModel = Roo.factory(this.selModel, Roo.tree);
31735     }
31736    
31737 };
31738 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31739     rootVisible : true,
31740     animate: Roo.enableFx,
31741     lines : true,
31742     enableDD : false,
31743     hlDrop : Roo.enableFx,
31744   
31745     renderer: false,
31746     
31747     rendererTip: false,
31748     // private
31749     restrictExpand : function(node){
31750         var p = node.parentNode;
31751         if(p){
31752             if(p.expandedChild && p.expandedChild.parentNode == p){
31753                 p.expandedChild.collapse();
31754             }
31755             p.expandedChild = node;
31756         }
31757     },
31758
31759     // private override
31760     setRootNode : function(node){
31761         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31762         if(!this.rootVisible){
31763             node.ui = new Roo.tree.RootTreeNodeUI(node);
31764         }
31765         return node;
31766     },
31767
31768     /**
31769      * Returns the container element for this TreePanel
31770      */
31771     getEl : function(){
31772         return this.el;
31773     },
31774
31775     /**
31776      * Returns the default TreeLoader for this TreePanel
31777      */
31778     getLoader : function(){
31779         return this.loader;
31780     },
31781
31782     /**
31783      * Expand all nodes
31784      */
31785     expandAll : function(){
31786         this.root.expand(true);
31787     },
31788
31789     /**
31790      * Collapse all nodes
31791      */
31792     collapseAll : function(){
31793         this.root.collapse(true);
31794     },
31795
31796     /**
31797      * Returns the selection model used by this TreePanel
31798      */
31799     getSelectionModel : function(){
31800         if(!this.selModel){
31801             this.selModel = new Roo.tree.DefaultSelectionModel();
31802         }
31803         return this.selModel;
31804     },
31805
31806     /**
31807      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31808      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31809      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31810      * @return {Array}
31811      */
31812     getChecked : function(a, startNode){
31813         startNode = startNode || this.root;
31814         var r = [];
31815         var f = function(){
31816             if(this.attributes.checked){
31817                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31818             }
31819         }
31820         startNode.cascade(f);
31821         return r;
31822     },
31823
31824     /**
31825      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31826      * @param {String} path
31827      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31828      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31829      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31830      */
31831     expandPath : function(path, attr, callback){
31832         attr = attr || "id";
31833         var keys = path.split(this.pathSeparator);
31834         var curNode = this.root;
31835         if(curNode.attributes[attr] != keys[1]){ // invalid root
31836             if(callback){
31837                 callback(false, null);
31838             }
31839             return;
31840         }
31841         var index = 1;
31842         var f = function(){
31843             if(++index == keys.length){
31844                 if(callback){
31845                     callback(true, curNode);
31846                 }
31847                 return;
31848             }
31849             var c = curNode.findChild(attr, keys[index]);
31850             if(!c){
31851                 if(callback){
31852                     callback(false, curNode);
31853                 }
31854                 return;
31855             }
31856             curNode = c;
31857             c.expand(false, false, f);
31858         };
31859         curNode.expand(false, false, f);
31860     },
31861
31862     /**
31863      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31864      * @param {String} path
31865      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31866      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31867      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31868      */
31869     selectPath : function(path, attr, callback){
31870         attr = attr || "id";
31871         var keys = path.split(this.pathSeparator);
31872         var v = keys.pop();
31873         if(keys.length > 0){
31874             var f = function(success, node){
31875                 if(success && node){
31876                     var n = node.findChild(attr, v);
31877                     if(n){
31878                         n.select();
31879                         if(callback){
31880                             callback(true, n);
31881                         }
31882                     }else if(callback){
31883                         callback(false, n);
31884                     }
31885                 }else{
31886                     if(callback){
31887                         callback(false, n);
31888                     }
31889                 }
31890             };
31891             this.expandPath(keys.join(this.pathSeparator), attr, f);
31892         }else{
31893             this.root.select();
31894             if(callback){
31895                 callback(true, this.root);
31896             }
31897         }
31898     },
31899
31900     getTreeEl : function(){
31901         return this.el;
31902     },
31903
31904     /**
31905      * Trigger rendering of this TreePanel
31906      */
31907     render : function(){
31908         if (this.innerCt) {
31909             return this; // stop it rendering more than once!!
31910         }
31911         
31912         this.innerCt = this.el.createChild({tag:"ul",
31913                cls:"x-tree-root-ct " +
31914                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31915
31916         if(this.containerScroll){
31917             Roo.dd.ScrollManager.register(this.el);
31918         }
31919         if((this.enableDD || this.enableDrop) && !this.dropZone){
31920            /**
31921             * The dropZone used by this tree if drop is enabled
31922             * @type Roo.tree.TreeDropZone
31923             */
31924              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31925                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31926            });
31927         }
31928         if((this.enableDD || this.enableDrag) && !this.dragZone){
31929            /**
31930             * The dragZone used by this tree if drag is enabled
31931             * @type Roo.tree.TreeDragZone
31932             */
31933             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31934                ddGroup: this.ddGroup || "TreeDD",
31935                scroll: this.ddScroll
31936            });
31937         }
31938         this.getSelectionModel().init(this);
31939         if (!this.root) {
31940             Roo.log("ROOT not set in tree");
31941             return this;
31942         }
31943         this.root.render();
31944         if(!this.rootVisible){
31945             this.root.renderChildren();
31946         }
31947         return this;
31948     }
31949 });/*
31950  * Based on:
31951  * Ext JS Library 1.1.1
31952  * Copyright(c) 2006-2007, Ext JS, LLC.
31953  *
31954  * Originally Released Under LGPL - original licence link has changed is not relivant.
31955  *
31956  * Fork - LGPL
31957  * <script type="text/javascript">
31958  */
31959  
31960
31961 /**
31962  * @class Roo.tree.DefaultSelectionModel
31963  * @extends Roo.util.Observable
31964  * The default single selection for a TreePanel.
31965  * @param {Object} cfg Configuration
31966  */
31967 Roo.tree.DefaultSelectionModel = function(cfg){
31968    this.selNode = null;
31969    
31970    
31971    
31972    this.addEvents({
31973        /**
31974         * @event selectionchange
31975         * Fires when the selected node changes
31976         * @param {DefaultSelectionModel} this
31977         * @param {TreeNode} node the new selection
31978         */
31979        "selectionchange" : true,
31980
31981        /**
31982         * @event beforeselect
31983         * Fires before the selected node changes, return false to cancel the change
31984         * @param {DefaultSelectionModel} this
31985         * @param {TreeNode} node the new selection
31986         * @param {TreeNode} node the old selection
31987         */
31988        "beforeselect" : true
31989    });
31990    
31991     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31992 };
31993
31994 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31995     init : function(tree){
31996         this.tree = tree;
31997         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31998         tree.on("click", this.onNodeClick, this);
31999     },
32000     
32001     onNodeClick : function(node, e){
32002         if (e.ctrlKey && this.selNode == node)  {
32003             this.unselect(node);
32004             return;
32005         }
32006         this.select(node);
32007     },
32008     
32009     /**
32010      * Select a node.
32011      * @param {TreeNode} node The node to select
32012      * @return {TreeNode} The selected node
32013      */
32014     select : function(node){
32015         var last = this.selNode;
32016         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32017             if(last){
32018                 last.ui.onSelectedChange(false);
32019             }
32020             this.selNode = node;
32021             node.ui.onSelectedChange(true);
32022             this.fireEvent("selectionchange", this, node, last);
32023         }
32024         return node;
32025     },
32026     
32027     /**
32028      * Deselect a node.
32029      * @param {TreeNode} node The node to unselect
32030      */
32031     unselect : function(node){
32032         if(this.selNode == node){
32033             this.clearSelections();
32034         }    
32035     },
32036     
32037     /**
32038      * Clear all selections
32039      */
32040     clearSelections : function(){
32041         var n = this.selNode;
32042         if(n){
32043             n.ui.onSelectedChange(false);
32044             this.selNode = null;
32045             this.fireEvent("selectionchange", this, null);
32046         }
32047         return n;
32048     },
32049     
32050     /**
32051      * Get the selected node
32052      * @return {TreeNode} The selected node
32053      */
32054     getSelectedNode : function(){
32055         return this.selNode;    
32056     },
32057     
32058     /**
32059      * Returns true if the node is selected
32060      * @param {TreeNode} node The node to check
32061      * @return {Boolean}
32062      */
32063     isSelected : function(node){
32064         return this.selNode == node;  
32065     },
32066
32067     /**
32068      * Selects the node above the selected node in the tree, intelligently walking the nodes
32069      * @return TreeNode The new selection
32070      */
32071     selectPrevious : function(){
32072         var s = this.selNode || this.lastSelNode;
32073         if(!s){
32074             return null;
32075         }
32076         var ps = s.previousSibling;
32077         if(ps){
32078             if(!ps.isExpanded() || ps.childNodes.length < 1){
32079                 return this.select(ps);
32080             } else{
32081                 var lc = ps.lastChild;
32082                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32083                     lc = lc.lastChild;
32084                 }
32085                 return this.select(lc);
32086             }
32087         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32088             return this.select(s.parentNode);
32089         }
32090         return null;
32091     },
32092
32093     /**
32094      * Selects the node above the selected node in the tree, intelligently walking the nodes
32095      * @return TreeNode The new selection
32096      */
32097     selectNext : function(){
32098         var s = this.selNode || this.lastSelNode;
32099         if(!s){
32100             return null;
32101         }
32102         if(s.firstChild && s.isExpanded()){
32103              return this.select(s.firstChild);
32104          }else if(s.nextSibling){
32105              return this.select(s.nextSibling);
32106          }else if(s.parentNode){
32107             var newS = null;
32108             s.parentNode.bubble(function(){
32109                 if(this.nextSibling){
32110                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32111                     return false;
32112                 }
32113             });
32114             return newS;
32115          }
32116         return null;
32117     },
32118
32119     onKeyDown : function(e){
32120         var s = this.selNode || this.lastSelNode;
32121         // undesirable, but required
32122         var sm = this;
32123         if(!s){
32124             return;
32125         }
32126         var k = e.getKey();
32127         switch(k){
32128              case e.DOWN:
32129                  e.stopEvent();
32130                  this.selectNext();
32131              break;
32132              case e.UP:
32133                  e.stopEvent();
32134                  this.selectPrevious();
32135              break;
32136              case e.RIGHT:
32137                  e.preventDefault();
32138                  if(s.hasChildNodes()){
32139                      if(!s.isExpanded()){
32140                          s.expand();
32141                      }else if(s.firstChild){
32142                          this.select(s.firstChild, e);
32143                      }
32144                  }
32145              break;
32146              case e.LEFT:
32147                  e.preventDefault();
32148                  if(s.hasChildNodes() && s.isExpanded()){
32149                      s.collapse();
32150                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32151                      this.select(s.parentNode, e);
32152                  }
32153              break;
32154         };
32155     }
32156 });
32157
32158 /**
32159  * @class Roo.tree.MultiSelectionModel
32160  * @extends Roo.util.Observable
32161  * Multi selection for a TreePanel.
32162  * @param {Object} cfg Configuration
32163  */
32164 Roo.tree.MultiSelectionModel = function(){
32165    this.selNodes = [];
32166    this.selMap = {};
32167    this.addEvents({
32168        /**
32169         * @event selectionchange
32170         * Fires when the selected nodes change
32171         * @param {MultiSelectionModel} this
32172         * @param {Array} nodes Array of the selected nodes
32173         */
32174        "selectionchange" : true
32175    });
32176    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32177    
32178 };
32179
32180 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32181     init : function(tree){
32182         this.tree = tree;
32183         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32184         tree.on("click", this.onNodeClick, this);
32185     },
32186     
32187     onNodeClick : function(node, e){
32188         this.select(node, e, e.ctrlKey);
32189     },
32190     
32191     /**
32192      * Select a node.
32193      * @param {TreeNode} node The node to select
32194      * @param {EventObject} e (optional) An event associated with the selection
32195      * @param {Boolean} keepExisting True to retain existing selections
32196      * @return {TreeNode} The selected node
32197      */
32198     select : function(node, e, keepExisting){
32199         if(keepExisting !== true){
32200             this.clearSelections(true);
32201         }
32202         if(this.isSelected(node)){
32203             this.lastSelNode = node;
32204             return node;
32205         }
32206         this.selNodes.push(node);
32207         this.selMap[node.id] = node;
32208         this.lastSelNode = node;
32209         node.ui.onSelectedChange(true);
32210         this.fireEvent("selectionchange", this, this.selNodes);
32211         return node;
32212     },
32213     
32214     /**
32215      * Deselect a node.
32216      * @param {TreeNode} node The node to unselect
32217      */
32218     unselect : function(node){
32219         if(this.selMap[node.id]){
32220             node.ui.onSelectedChange(false);
32221             var sn = this.selNodes;
32222             var index = -1;
32223             if(sn.indexOf){
32224                 index = sn.indexOf(node);
32225             }else{
32226                 for(var i = 0, len = sn.length; i < len; i++){
32227                     if(sn[i] == node){
32228                         index = i;
32229                         break;
32230                     }
32231                 }
32232             }
32233             if(index != -1){
32234                 this.selNodes.splice(index, 1);
32235             }
32236             delete this.selMap[node.id];
32237             this.fireEvent("selectionchange", this, this.selNodes);
32238         }
32239     },
32240     
32241     /**
32242      * Clear all selections
32243      */
32244     clearSelections : function(suppressEvent){
32245         var sn = this.selNodes;
32246         if(sn.length > 0){
32247             for(var i = 0, len = sn.length; i < len; i++){
32248                 sn[i].ui.onSelectedChange(false);
32249             }
32250             this.selNodes = [];
32251             this.selMap = {};
32252             if(suppressEvent !== true){
32253                 this.fireEvent("selectionchange", this, this.selNodes);
32254             }
32255         }
32256     },
32257     
32258     /**
32259      * Returns true if the node is selected
32260      * @param {TreeNode} node The node to check
32261      * @return {Boolean}
32262      */
32263     isSelected : function(node){
32264         return this.selMap[node.id] ? true : false;  
32265     },
32266     
32267     /**
32268      * Returns an array of the selected nodes
32269      * @return {Array}
32270      */
32271     getSelectedNodes : function(){
32272         return this.selNodes;    
32273     },
32274
32275     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32276
32277     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32278
32279     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32280 });/*
32281  * Based on:
32282  * Ext JS Library 1.1.1
32283  * Copyright(c) 2006-2007, Ext JS, LLC.
32284  *
32285  * Originally Released Under LGPL - original licence link has changed is not relivant.
32286  *
32287  * Fork - LGPL
32288  * <script type="text/javascript">
32289  */
32290  
32291 /**
32292  * @class Roo.tree.TreeNode
32293  * @extends Roo.data.Node
32294  * @cfg {String} text The text for this node
32295  * @cfg {Boolean} expanded true to start the node expanded
32296  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32297  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32298  * @cfg {Boolean} disabled true to start the node disabled
32299  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32300  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32301  * @cfg {String} cls A css class to be added to the node
32302  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32303  * @cfg {String} href URL of the link used for the node (defaults to #)
32304  * @cfg {String} hrefTarget target frame for the link
32305  * @cfg {String} qtip An Ext QuickTip for the node
32306  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32307  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32308  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32309  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32310  * (defaults to undefined with no checkbox rendered)
32311  * @constructor
32312  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32313  */
32314 Roo.tree.TreeNode = function(attributes){
32315     attributes = attributes || {};
32316     if(typeof attributes == "string"){
32317         attributes = {text: attributes};
32318     }
32319     this.childrenRendered = false;
32320     this.rendered = false;
32321     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32322     this.expanded = attributes.expanded === true;
32323     this.isTarget = attributes.isTarget !== false;
32324     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32325     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32326
32327     /**
32328      * Read-only. The text for this node. To change it use setText().
32329      * @type String
32330      */
32331     this.text = attributes.text;
32332     /**
32333      * True if this node is disabled.
32334      * @type Boolean
32335      */
32336     this.disabled = attributes.disabled === true;
32337
32338     this.addEvents({
32339         /**
32340         * @event textchange
32341         * Fires when the text for this node is changed
32342         * @param {Node} this This node
32343         * @param {String} text The new text
32344         * @param {String} oldText The old text
32345         */
32346         "textchange" : true,
32347         /**
32348         * @event beforeexpand
32349         * Fires before this node is expanded, return false to cancel.
32350         * @param {Node} this This node
32351         * @param {Boolean} deep
32352         * @param {Boolean} anim
32353         */
32354         "beforeexpand" : true,
32355         /**
32356         * @event beforecollapse
32357         * Fires before this node is collapsed, return false to cancel.
32358         * @param {Node} this This node
32359         * @param {Boolean} deep
32360         * @param {Boolean} anim
32361         */
32362         "beforecollapse" : true,
32363         /**
32364         * @event expand
32365         * Fires when this node is expanded
32366         * @param {Node} this This node
32367         */
32368         "expand" : true,
32369         /**
32370         * @event disabledchange
32371         * Fires when the disabled status of this node changes
32372         * @param {Node} this This node
32373         * @param {Boolean} disabled
32374         */
32375         "disabledchange" : true,
32376         /**
32377         * @event collapse
32378         * Fires when this node is collapsed
32379         * @param {Node} this This node
32380         */
32381         "collapse" : true,
32382         /**
32383         * @event beforeclick
32384         * Fires before click processing. Return false to cancel the default action.
32385         * @param {Node} this This node
32386         * @param {Roo.EventObject} e The event object
32387         */
32388         "beforeclick":true,
32389         /**
32390         * @event checkchange
32391         * Fires when a node with a checkbox's checked property changes
32392         * @param {Node} this This node
32393         * @param {Boolean} checked
32394         */
32395         "checkchange":true,
32396         /**
32397         * @event click
32398         * Fires when this node is clicked
32399         * @param {Node} this This node
32400         * @param {Roo.EventObject} e The event object
32401         */
32402         "click":true,
32403         /**
32404         * @event dblclick
32405         * Fires when this node is double clicked
32406         * @param {Node} this This node
32407         * @param {Roo.EventObject} e The event object
32408         */
32409         "dblclick":true,
32410         /**
32411         * @event contextmenu
32412         * Fires when this node is right clicked
32413         * @param {Node} this This node
32414         * @param {Roo.EventObject} e The event object
32415         */
32416         "contextmenu":true,
32417         /**
32418         * @event beforechildrenrendered
32419         * Fires right before the child nodes for this node are rendered
32420         * @param {Node} this This node
32421         */
32422         "beforechildrenrendered":true
32423     });
32424
32425     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
32426
32427     /**
32428      * Read-only. The UI for this node
32429      * @type TreeNodeUI
32430      */
32431     this.ui = new uiClass(this);
32432     
32433     // finally support items[]
32434     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
32435         return;
32436     }
32437     
32438     
32439     Roo.each(this.attributes.items, function(c) {
32440         this.appendChild(Roo.factory(c,Roo.Tree));
32441     }, this);
32442     delete this.attributes.items;
32443     
32444     
32445     
32446 };
32447 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
32448     preventHScroll: true,
32449     /**
32450      * Returns true if this node is expanded
32451      * @return {Boolean}
32452      */
32453     isExpanded : function(){
32454         return this.expanded;
32455     },
32456
32457     /**
32458      * Returns the UI object for this node
32459      * @return {TreeNodeUI}
32460      */
32461     getUI : function(){
32462         return this.ui;
32463     },
32464
32465     // private override
32466     setFirstChild : function(node){
32467         var of = this.firstChild;
32468         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
32469         if(this.childrenRendered && of && node != of){
32470             of.renderIndent(true, true);
32471         }
32472         if(this.rendered){
32473             this.renderIndent(true, true);
32474         }
32475     },
32476
32477     // private override
32478     setLastChild : function(node){
32479         var ol = this.lastChild;
32480         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
32481         if(this.childrenRendered && ol && node != ol){
32482             ol.renderIndent(true, true);
32483         }
32484         if(this.rendered){
32485             this.renderIndent(true, true);
32486         }
32487     },
32488
32489     // these methods are overridden to provide lazy rendering support
32490     // private override
32491     appendChild : function()
32492     {
32493         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
32494         if(node && this.childrenRendered){
32495             node.render();
32496         }
32497         this.ui.updateExpandIcon();
32498         return node;
32499     },
32500
32501     // private override
32502     removeChild : function(node){
32503         this.ownerTree.getSelectionModel().unselect(node);
32504         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
32505         // if it's been rendered remove dom node
32506         if(this.childrenRendered){
32507             node.ui.remove();
32508         }
32509         if(this.childNodes.length < 1){
32510             this.collapse(false, false);
32511         }else{
32512             this.ui.updateExpandIcon();
32513         }
32514         if(!this.firstChild) {
32515             this.childrenRendered = false;
32516         }
32517         return node;
32518     },
32519
32520     // private override
32521     insertBefore : function(node, refNode){
32522         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
32523         if(newNode && refNode && this.childrenRendered){
32524             node.render();
32525         }
32526         this.ui.updateExpandIcon();
32527         return newNode;
32528     },
32529
32530     /**
32531      * Sets the text for this node
32532      * @param {String} text
32533      */
32534     setText : function(text){
32535         var oldText = this.text;
32536         this.text = text;
32537         this.attributes.text = text;
32538         if(this.rendered){ // event without subscribing
32539             this.ui.onTextChange(this, text, oldText);
32540         }
32541         this.fireEvent("textchange", this, text, oldText);
32542     },
32543
32544     /**
32545      * Triggers selection of this node
32546      */
32547     select : function(){
32548         this.getOwnerTree().getSelectionModel().select(this);
32549     },
32550
32551     /**
32552      * Triggers deselection of this node
32553      */
32554     unselect : function(){
32555         this.getOwnerTree().getSelectionModel().unselect(this);
32556     },
32557
32558     /**
32559      * Returns true if this node is selected
32560      * @return {Boolean}
32561      */
32562     isSelected : function(){
32563         return this.getOwnerTree().getSelectionModel().isSelected(this);
32564     },
32565
32566     /**
32567      * Expand this node.
32568      * @param {Boolean} deep (optional) True to expand all children as well
32569      * @param {Boolean} anim (optional) false to cancel the default animation
32570      * @param {Function} callback (optional) A callback to be called when
32571      * expanding this node completes (does not wait for deep expand to complete).
32572      * Called with 1 parameter, this node.
32573      */
32574     expand : function(deep, anim, callback){
32575         if(!this.expanded){
32576             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
32577                 return;
32578             }
32579             if(!this.childrenRendered){
32580                 this.renderChildren();
32581             }
32582             this.expanded = true;
32583             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
32584                 this.ui.animExpand(function(){
32585                     this.fireEvent("expand", this);
32586                     if(typeof callback == "function"){
32587                         callback(this);
32588                     }
32589                     if(deep === true){
32590                         this.expandChildNodes(true);
32591                     }
32592                 }.createDelegate(this));
32593                 return;
32594             }else{
32595                 this.ui.expand();
32596                 this.fireEvent("expand", this);
32597                 if(typeof callback == "function"){
32598                     callback(this);
32599                 }
32600             }
32601         }else{
32602            if(typeof callback == "function"){
32603                callback(this);
32604            }
32605         }
32606         if(deep === true){
32607             this.expandChildNodes(true);
32608         }
32609     },
32610
32611     isHiddenRoot : function(){
32612         return this.isRoot && !this.getOwnerTree().rootVisible;
32613     },
32614
32615     /**
32616      * Collapse this node.
32617      * @param {Boolean} deep (optional) True to collapse all children as well
32618      * @param {Boolean} anim (optional) false to cancel the default animation
32619      */
32620     collapse : function(deep, anim){
32621         if(this.expanded && !this.isHiddenRoot()){
32622             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32623                 return;
32624             }
32625             this.expanded = false;
32626             if((this.getOwnerTree().animate && anim !== false) || anim){
32627                 this.ui.animCollapse(function(){
32628                     this.fireEvent("collapse", this);
32629                     if(deep === true){
32630                         this.collapseChildNodes(true);
32631                     }
32632                 }.createDelegate(this));
32633                 return;
32634             }else{
32635                 this.ui.collapse();
32636                 this.fireEvent("collapse", this);
32637             }
32638         }
32639         if(deep === true){
32640             var cs = this.childNodes;
32641             for(var i = 0, len = cs.length; i < len; i++) {
32642                 cs[i].collapse(true, false);
32643             }
32644         }
32645     },
32646
32647     // private
32648     delayedExpand : function(delay){
32649         if(!this.expandProcId){
32650             this.expandProcId = this.expand.defer(delay, this);
32651         }
32652     },
32653
32654     // private
32655     cancelExpand : function(){
32656         if(this.expandProcId){
32657             clearTimeout(this.expandProcId);
32658         }
32659         this.expandProcId = false;
32660     },
32661
32662     /**
32663      * Toggles expanded/collapsed state of the node
32664      */
32665     toggle : function(){
32666         if(this.expanded){
32667             this.collapse();
32668         }else{
32669             this.expand();
32670         }
32671     },
32672
32673     /**
32674      * Ensures all parent nodes are expanded
32675      */
32676     ensureVisible : function(callback){
32677         var tree = this.getOwnerTree();
32678         tree.expandPath(this.parentNode.getPath(), false, function(){
32679             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32680             Roo.callback(callback);
32681         }.createDelegate(this));
32682     },
32683
32684     /**
32685      * Expand all child nodes
32686      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32687      */
32688     expandChildNodes : function(deep){
32689         var cs = this.childNodes;
32690         for(var i = 0, len = cs.length; i < len; i++) {
32691                 cs[i].expand(deep);
32692         }
32693     },
32694
32695     /**
32696      * Collapse all child nodes
32697      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32698      */
32699     collapseChildNodes : function(deep){
32700         var cs = this.childNodes;
32701         for(var i = 0, len = cs.length; i < len; i++) {
32702                 cs[i].collapse(deep);
32703         }
32704     },
32705
32706     /**
32707      * Disables this node
32708      */
32709     disable : function(){
32710         this.disabled = true;
32711         this.unselect();
32712         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32713             this.ui.onDisableChange(this, true);
32714         }
32715         this.fireEvent("disabledchange", this, true);
32716     },
32717
32718     /**
32719      * Enables this node
32720      */
32721     enable : function(){
32722         this.disabled = false;
32723         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32724             this.ui.onDisableChange(this, false);
32725         }
32726         this.fireEvent("disabledchange", this, false);
32727     },
32728
32729     // private
32730     renderChildren : function(suppressEvent){
32731         if(suppressEvent !== false){
32732             this.fireEvent("beforechildrenrendered", this);
32733         }
32734         var cs = this.childNodes;
32735         for(var i = 0, len = cs.length; i < len; i++){
32736             cs[i].render(true);
32737         }
32738         this.childrenRendered = true;
32739     },
32740
32741     // private
32742     sort : function(fn, scope){
32743         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32744         if(this.childrenRendered){
32745             var cs = this.childNodes;
32746             for(var i = 0, len = cs.length; i < len; i++){
32747                 cs[i].render(true);
32748             }
32749         }
32750     },
32751
32752     // private
32753     render : function(bulkRender){
32754         this.ui.render(bulkRender);
32755         if(!this.rendered){
32756             this.rendered = true;
32757             if(this.expanded){
32758                 this.expanded = false;
32759                 this.expand(false, false);
32760             }
32761         }
32762     },
32763
32764     // private
32765     renderIndent : function(deep, refresh){
32766         if(refresh){
32767             this.ui.childIndent = null;
32768         }
32769         this.ui.renderIndent();
32770         if(deep === true && this.childrenRendered){
32771             var cs = this.childNodes;
32772             for(var i = 0, len = cs.length; i < len; i++){
32773                 cs[i].renderIndent(true, refresh);
32774             }
32775         }
32776     }
32777 });/*
32778  * Based on:
32779  * Ext JS Library 1.1.1
32780  * Copyright(c) 2006-2007, Ext JS, LLC.
32781  *
32782  * Originally Released Under LGPL - original licence link has changed is not relivant.
32783  *
32784  * Fork - LGPL
32785  * <script type="text/javascript">
32786  */
32787  
32788 /**
32789  * @class Roo.tree.AsyncTreeNode
32790  * @extends Roo.tree.TreeNode
32791  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32792  * @constructor
32793  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32794  */
32795  Roo.tree.AsyncTreeNode = function(config){
32796     this.loaded = false;
32797     this.loading = false;
32798     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32799     /**
32800     * @event beforeload
32801     * Fires before this node is loaded, return false to cancel
32802     * @param {Node} this This node
32803     */
32804     this.addEvents({'beforeload':true, 'load': true});
32805     /**
32806     * @event load
32807     * Fires when this node is loaded
32808     * @param {Node} this This node
32809     */
32810     /**
32811      * The loader used by this node (defaults to using the tree's defined loader)
32812      * @type TreeLoader
32813      * @property loader
32814      */
32815 };
32816 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32817     expand : function(deep, anim, callback){
32818         if(this.loading){ // if an async load is already running, waiting til it's done
32819             var timer;
32820             var f = function(){
32821                 if(!this.loading){ // done loading
32822                     clearInterval(timer);
32823                     this.expand(deep, anim, callback);
32824                 }
32825             }.createDelegate(this);
32826             timer = setInterval(f, 200);
32827             return;
32828         }
32829         if(!this.loaded){
32830             if(this.fireEvent("beforeload", this) === false){
32831                 return;
32832             }
32833             this.loading = true;
32834             this.ui.beforeLoad(this);
32835             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32836             if(loader){
32837                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32838                 return;
32839             }
32840         }
32841         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32842     },
32843     
32844     /**
32845      * Returns true if this node is currently loading
32846      * @return {Boolean}
32847      */
32848     isLoading : function(){
32849         return this.loading;  
32850     },
32851     
32852     loadComplete : function(deep, anim, callback){
32853         this.loading = false;
32854         this.loaded = true;
32855         this.ui.afterLoad(this);
32856         this.fireEvent("load", this);
32857         this.expand(deep, anim, callback);
32858     },
32859     
32860     /**
32861      * Returns true if this node has been loaded
32862      * @return {Boolean}
32863      */
32864     isLoaded : function(){
32865         return this.loaded;
32866     },
32867     
32868     hasChildNodes : function(){
32869         if(!this.isLeaf() && !this.loaded){
32870             return true;
32871         }else{
32872             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32873         }
32874     },
32875
32876     /**
32877      * Trigger a reload for this node
32878      * @param {Function} callback
32879      */
32880     reload : function(callback){
32881         this.collapse(false, false);
32882         while(this.firstChild){
32883             this.removeChild(this.firstChild);
32884         }
32885         this.childrenRendered = false;
32886         this.loaded = false;
32887         if(this.isHiddenRoot()){
32888             this.expanded = false;
32889         }
32890         this.expand(false, false, callback);
32891     }
32892 });/*
32893  * Based on:
32894  * Ext JS Library 1.1.1
32895  * Copyright(c) 2006-2007, Ext JS, LLC.
32896  *
32897  * Originally Released Under LGPL - original licence link has changed is not relivant.
32898  *
32899  * Fork - LGPL
32900  * <script type="text/javascript">
32901  */
32902  
32903 /**
32904  * @class Roo.tree.TreeNodeUI
32905  * @constructor
32906  * @param {Object} node The node to render
32907  * The TreeNode UI implementation is separate from the
32908  * tree implementation. Unless you are customizing the tree UI,
32909  * you should never have to use this directly.
32910  */
32911 Roo.tree.TreeNodeUI = function(node){
32912     this.node = node;
32913     this.rendered = false;
32914     this.animating = false;
32915     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32916 };
32917
32918 Roo.tree.TreeNodeUI.prototype = {
32919     removeChild : function(node){
32920         if(this.rendered){
32921             this.ctNode.removeChild(node.ui.getEl());
32922         }
32923     },
32924
32925     beforeLoad : function(){
32926          this.addClass("x-tree-node-loading");
32927     },
32928
32929     afterLoad : function(){
32930          this.removeClass("x-tree-node-loading");
32931     },
32932
32933     onTextChange : function(node, text, oldText){
32934         if(this.rendered){
32935             this.textNode.innerHTML = text;
32936         }
32937     },
32938
32939     onDisableChange : function(node, state){
32940         this.disabled = state;
32941         if(state){
32942             this.addClass("x-tree-node-disabled");
32943         }else{
32944             this.removeClass("x-tree-node-disabled");
32945         }
32946     },
32947
32948     onSelectedChange : function(state){
32949         if(state){
32950             this.focus();
32951             this.addClass("x-tree-selected");
32952         }else{
32953             //this.blur();
32954             this.removeClass("x-tree-selected");
32955         }
32956     },
32957
32958     onMove : function(tree, node, oldParent, newParent, index, refNode){
32959         this.childIndent = null;
32960         if(this.rendered){
32961             var targetNode = newParent.ui.getContainer();
32962             if(!targetNode){//target not rendered
32963                 this.holder = document.createElement("div");
32964                 this.holder.appendChild(this.wrap);
32965                 return;
32966             }
32967             var insertBefore = refNode ? refNode.ui.getEl() : null;
32968             if(insertBefore){
32969                 targetNode.insertBefore(this.wrap, insertBefore);
32970             }else{
32971                 targetNode.appendChild(this.wrap);
32972             }
32973             this.node.renderIndent(true);
32974         }
32975     },
32976
32977     addClass : function(cls){
32978         if(this.elNode){
32979             Roo.fly(this.elNode).addClass(cls);
32980         }
32981     },
32982
32983     removeClass : function(cls){
32984         if(this.elNode){
32985             Roo.fly(this.elNode).removeClass(cls);
32986         }
32987     },
32988
32989     remove : function(){
32990         if(this.rendered){
32991             this.holder = document.createElement("div");
32992             this.holder.appendChild(this.wrap);
32993         }
32994     },
32995
32996     fireEvent : function(){
32997         return this.node.fireEvent.apply(this.node, arguments);
32998     },
32999
33000     initEvents : function(){
33001         this.node.on("move", this.onMove, this);
33002         var E = Roo.EventManager;
33003         var a = this.anchor;
33004
33005         var el = Roo.fly(a, '_treeui');
33006
33007         if(Roo.isOpera){ // opera render bug ignores the CSS
33008             el.setStyle("text-decoration", "none");
33009         }
33010
33011         el.on("click", this.onClick, this);
33012         el.on("dblclick", this.onDblClick, this);
33013
33014         if(this.checkbox){
33015             Roo.EventManager.on(this.checkbox,
33016                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33017         }
33018
33019         el.on("contextmenu", this.onContextMenu, this);
33020
33021         var icon = Roo.fly(this.iconNode);
33022         icon.on("click", this.onClick, this);
33023         icon.on("dblclick", this.onDblClick, this);
33024         icon.on("contextmenu", this.onContextMenu, this);
33025         E.on(this.ecNode, "click", this.ecClick, this, true);
33026
33027         if(this.node.disabled){
33028             this.addClass("x-tree-node-disabled");
33029         }
33030         if(this.node.hidden){
33031             this.addClass("x-tree-node-disabled");
33032         }
33033         var ot = this.node.getOwnerTree();
33034         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33035         if(dd && (!this.node.isRoot || ot.rootVisible)){
33036             Roo.dd.Registry.register(this.elNode, {
33037                 node: this.node,
33038                 handles: this.getDDHandles(),
33039                 isHandle: false
33040             });
33041         }
33042     },
33043
33044     getDDHandles : function(){
33045         return [this.iconNode, this.textNode];
33046     },
33047
33048     hide : function(){
33049         if(this.rendered){
33050             this.wrap.style.display = "none";
33051         }
33052     },
33053
33054     show : function(){
33055         if(this.rendered){
33056             this.wrap.style.display = "";
33057         }
33058     },
33059
33060     onContextMenu : function(e){
33061         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33062             e.preventDefault();
33063             this.focus();
33064             this.fireEvent("contextmenu", this.node, e);
33065         }
33066     },
33067
33068     onClick : function(e){
33069         if(this.dropping){
33070             e.stopEvent();
33071             return;
33072         }
33073         if(this.fireEvent("beforeclick", this.node, e) !== false){
33074             if(!this.disabled && this.node.attributes.href){
33075                 this.fireEvent("click", this.node, e);
33076                 return;
33077             }
33078             e.preventDefault();
33079             if(this.disabled){
33080                 return;
33081             }
33082
33083             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33084                 this.node.toggle();
33085             }
33086
33087             this.fireEvent("click", this.node, e);
33088         }else{
33089             e.stopEvent();
33090         }
33091     },
33092
33093     onDblClick : function(e){
33094         e.preventDefault();
33095         if(this.disabled){
33096             return;
33097         }
33098         if(this.checkbox){
33099             this.toggleCheck();
33100         }
33101         if(!this.animating && this.node.hasChildNodes()){
33102             this.node.toggle();
33103         }
33104         this.fireEvent("dblclick", this.node, e);
33105     },
33106
33107     onCheckChange : function(){
33108         var checked = this.checkbox.checked;
33109         this.node.attributes.checked = checked;
33110         this.fireEvent('checkchange', this.node, checked);
33111     },
33112
33113     ecClick : function(e){
33114         if(!this.animating && this.node.hasChildNodes()){
33115             this.node.toggle();
33116         }
33117     },
33118
33119     startDrop : function(){
33120         this.dropping = true;
33121     },
33122
33123     // delayed drop so the click event doesn't get fired on a drop
33124     endDrop : function(){
33125        setTimeout(function(){
33126            this.dropping = false;
33127        }.createDelegate(this), 50);
33128     },
33129
33130     expand : function(){
33131         this.updateExpandIcon();
33132         this.ctNode.style.display = "";
33133     },
33134
33135     focus : function(){
33136         if(!this.node.preventHScroll){
33137             try{this.anchor.focus();
33138             }catch(e){}
33139         }else if(!Roo.isIE){
33140             try{
33141                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33142                 var l = noscroll.scrollLeft;
33143                 this.anchor.focus();
33144                 noscroll.scrollLeft = l;
33145             }catch(e){}
33146         }
33147     },
33148
33149     toggleCheck : function(value){
33150         var cb = this.checkbox;
33151         if(cb){
33152             cb.checked = (value === undefined ? !cb.checked : value);
33153         }
33154     },
33155
33156     blur : function(){
33157         try{
33158             this.anchor.blur();
33159         }catch(e){}
33160     },
33161
33162     animExpand : function(callback){
33163         var ct = Roo.get(this.ctNode);
33164         ct.stopFx();
33165         if(!this.node.hasChildNodes()){
33166             this.updateExpandIcon();
33167             this.ctNode.style.display = "";
33168             Roo.callback(callback);
33169             return;
33170         }
33171         this.animating = true;
33172         this.updateExpandIcon();
33173
33174         ct.slideIn('t', {
33175            callback : function(){
33176                this.animating = false;
33177                Roo.callback(callback);
33178             },
33179             scope: this,
33180             duration: this.node.ownerTree.duration || .25
33181         });
33182     },
33183
33184     highlight : function(){
33185         var tree = this.node.getOwnerTree();
33186         Roo.fly(this.wrap).highlight(
33187             tree.hlColor || "C3DAF9",
33188             {endColor: tree.hlBaseColor}
33189         );
33190     },
33191
33192     collapse : function(){
33193         this.updateExpandIcon();
33194         this.ctNode.style.display = "none";
33195     },
33196
33197     animCollapse : function(callback){
33198         var ct = Roo.get(this.ctNode);
33199         ct.enableDisplayMode('block');
33200         ct.stopFx();
33201
33202         this.animating = true;
33203         this.updateExpandIcon();
33204
33205         ct.slideOut('t', {
33206             callback : function(){
33207                this.animating = false;
33208                Roo.callback(callback);
33209             },
33210             scope: this,
33211             duration: this.node.ownerTree.duration || .25
33212         });
33213     },
33214
33215     getContainer : function(){
33216         return this.ctNode;
33217     },
33218
33219     getEl : function(){
33220         return this.wrap;
33221     },
33222
33223     appendDDGhost : function(ghostNode){
33224         ghostNode.appendChild(this.elNode.cloneNode(true));
33225     },
33226
33227     getDDRepairXY : function(){
33228         return Roo.lib.Dom.getXY(this.iconNode);
33229     },
33230
33231     onRender : function(){
33232         this.render();
33233     },
33234
33235     render : function(bulkRender){
33236         var n = this.node, a = n.attributes;
33237         var targetNode = n.parentNode ?
33238               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33239
33240         if(!this.rendered){
33241             this.rendered = true;
33242
33243             this.renderElements(n, a, targetNode, bulkRender);
33244
33245             if(a.qtip){
33246                if(this.textNode.setAttributeNS){
33247                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33248                    if(a.qtipTitle){
33249                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33250                    }
33251                }else{
33252                    this.textNode.setAttribute("ext:qtip", a.qtip);
33253                    if(a.qtipTitle){
33254                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33255                    }
33256                }
33257             }else if(a.qtipCfg){
33258                 a.qtipCfg.target = Roo.id(this.textNode);
33259                 Roo.QuickTips.register(a.qtipCfg);
33260             }
33261             this.initEvents();
33262             if(!this.node.expanded){
33263                 this.updateExpandIcon();
33264             }
33265         }else{
33266             if(bulkRender === true) {
33267                 targetNode.appendChild(this.wrap);
33268             }
33269         }
33270     },
33271
33272     renderElements : function(n, a, targetNode, bulkRender)
33273     {
33274         // add some indent caching, this helps performance when rendering a large tree
33275         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33276         var t = n.getOwnerTree();
33277         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33278         if (typeof(n.attributes.html) != 'undefined') {
33279             txt = n.attributes.html;
33280         }
33281         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33282         var cb = typeof a.checked == 'boolean';
33283         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33284         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33285             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33286             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33287             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33288             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33289             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33290              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33291                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33292             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33293             "</li>"];
33294
33295         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33296             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33297                                 n.nextSibling.ui.getEl(), buf.join(""));
33298         }else{
33299             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33300         }
33301
33302         this.elNode = this.wrap.childNodes[0];
33303         this.ctNode = this.wrap.childNodes[1];
33304         var cs = this.elNode.childNodes;
33305         this.indentNode = cs[0];
33306         this.ecNode = cs[1];
33307         this.iconNode = cs[2];
33308         var index = 3;
33309         if(cb){
33310             this.checkbox = cs[3];
33311             index++;
33312         }
33313         this.anchor = cs[index];
33314         this.textNode = cs[index].firstChild;
33315     },
33316
33317     getAnchor : function(){
33318         return this.anchor;
33319     },
33320
33321     getTextEl : function(){
33322         return this.textNode;
33323     },
33324
33325     getIconEl : function(){
33326         return this.iconNode;
33327     },
33328
33329     isChecked : function(){
33330         return this.checkbox ? this.checkbox.checked : false;
33331     },
33332
33333     updateExpandIcon : function(){
33334         if(this.rendered){
33335             var n = this.node, c1, c2;
33336             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
33337             var hasChild = n.hasChildNodes();
33338             if(hasChild){
33339                 if(n.expanded){
33340                     cls += "-minus";
33341                     c1 = "x-tree-node-collapsed";
33342                     c2 = "x-tree-node-expanded";
33343                 }else{
33344                     cls += "-plus";
33345                     c1 = "x-tree-node-expanded";
33346                     c2 = "x-tree-node-collapsed";
33347                 }
33348                 if(this.wasLeaf){
33349                     this.removeClass("x-tree-node-leaf");
33350                     this.wasLeaf = false;
33351                 }
33352                 if(this.c1 != c1 || this.c2 != c2){
33353                     Roo.fly(this.elNode).replaceClass(c1, c2);
33354                     this.c1 = c1; this.c2 = c2;
33355                 }
33356             }else{
33357                 // this changes non-leafs into leafs if they have no children.
33358                 // it's not very rational behaviour..
33359                 
33360                 if(!this.wasLeaf && this.node.leaf){
33361                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
33362                     delete this.c1;
33363                     delete this.c2;
33364                     this.wasLeaf = true;
33365                 }
33366             }
33367             var ecc = "x-tree-ec-icon "+cls;
33368             if(this.ecc != ecc){
33369                 this.ecNode.className = ecc;
33370                 this.ecc = ecc;
33371             }
33372         }
33373     },
33374
33375     getChildIndent : function(){
33376         if(!this.childIndent){
33377             var buf = [];
33378             var p = this.node;
33379             while(p){
33380                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
33381                     if(!p.isLast()) {
33382                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
33383                     } else {
33384                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
33385                     }
33386                 }
33387                 p = p.parentNode;
33388             }
33389             this.childIndent = buf.join("");
33390         }
33391         return this.childIndent;
33392     },
33393
33394     renderIndent : function(){
33395         if(this.rendered){
33396             var indent = "";
33397             var p = this.node.parentNode;
33398             if(p){
33399                 indent = p.ui.getChildIndent();
33400             }
33401             if(this.indentMarkup != indent){ // don't rerender if not required
33402                 this.indentNode.innerHTML = indent;
33403                 this.indentMarkup = indent;
33404             }
33405             this.updateExpandIcon();
33406         }
33407     }
33408 };
33409
33410 Roo.tree.RootTreeNodeUI = function(){
33411     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
33412 };
33413 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
33414     render : function(){
33415         if(!this.rendered){
33416             var targetNode = this.node.ownerTree.innerCt.dom;
33417             this.node.expanded = true;
33418             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
33419             this.wrap = this.ctNode = targetNode.firstChild;
33420         }
33421     },
33422     collapse : function(){
33423     },
33424     expand : function(){
33425     }
33426 });/*
33427  * Based on:
33428  * Ext JS Library 1.1.1
33429  * Copyright(c) 2006-2007, Ext JS, LLC.
33430  *
33431  * Originally Released Under LGPL - original licence link has changed is not relivant.
33432  *
33433  * Fork - LGPL
33434  * <script type="text/javascript">
33435  */
33436 /**
33437  * @class Roo.tree.TreeLoader
33438  * @extends Roo.util.Observable
33439  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
33440  * nodes from a specified URL. The response must be a javascript Array definition
33441  * who's elements are node definition objects. eg:
33442  * <pre><code>
33443 {  success : true,
33444    data :      [
33445    
33446     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
33447     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
33448     ]
33449 }
33450
33451
33452 </code></pre>
33453  * <br><br>
33454  * The old style respose with just an array is still supported, but not recommended.
33455  * <br><br>
33456  *
33457  * A server request is sent, and child nodes are loaded only when a node is expanded.
33458  * The loading node's id is passed to the server under the parameter name "node" to
33459  * enable the server to produce the correct child nodes.
33460  * <br><br>
33461  * To pass extra parameters, an event handler may be attached to the "beforeload"
33462  * event, and the parameters specified in the TreeLoader's baseParams property:
33463  * <pre><code>
33464     myTreeLoader.on("beforeload", function(treeLoader, node) {
33465         this.baseParams.category = node.attributes.category;
33466     }, this);
33467 </code></pre><
33468  * This would pass an HTTP parameter called "category" to the server containing
33469  * the value of the Node's "category" attribute.
33470  * @constructor
33471  * Creates a new Treeloader.
33472  * @param {Object} config A config object containing config properties.
33473  */
33474 Roo.tree.TreeLoader = function(config){
33475     this.baseParams = {};
33476     this.requestMethod = "POST";
33477     Roo.apply(this, config);
33478
33479     this.addEvents({
33480     
33481         /**
33482          * @event beforeload
33483          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
33484          * @param {Object} This TreeLoader object.
33485          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33486          * @param {Object} callback The callback function specified in the {@link #load} call.
33487          */
33488         beforeload : true,
33489         /**
33490          * @event load
33491          * Fires when the node has been successfuly loaded.
33492          * @param {Object} This TreeLoader object.
33493          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33494          * @param {Object} response The response object containing the data from the server.
33495          */
33496         load : true,
33497         /**
33498          * @event loadexception
33499          * Fires if the network request failed.
33500          * @param {Object} This TreeLoader object.
33501          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33502          * @param {Object} response The response object containing the data from the server.
33503          */
33504         loadexception : true,
33505         /**
33506          * @event create
33507          * Fires before a node is created, enabling you to return custom Node types 
33508          * @param {Object} This TreeLoader object.
33509          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
33510          */
33511         create : true
33512     });
33513
33514     Roo.tree.TreeLoader.superclass.constructor.call(this);
33515 };
33516
33517 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
33518     /**
33519     * @cfg {String} dataUrl The URL from which to request a Json string which
33520     * specifies an array of node definition object representing the child nodes
33521     * to be loaded.
33522     */
33523     /**
33524     * @cfg {String} requestMethod either GET or POST
33525     * defaults to POST (due to BC)
33526     * to be loaded.
33527     */
33528     /**
33529     * @cfg {Object} baseParams (optional) An object containing properties which
33530     * specify HTTP parameters to be passed to each request for child nodes.
33531     */
33532     /**
33533     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
33534     * created by this loader. If the attributes sent by the server have an attribute in this object,
33535     * they take priority.
33536     */
33537     /**
33538     * @cfg {Object} uiProviders (optional) An object containing properties which
33539     * 
33540     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
33541     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
33542     * <i>uiProvider</i> attribute of a returned child node is a string rather
33543     * than a reference to a TreeNodeUI implementation, this that string value
33544     * is used as a property name in the uiProviders object. You can define the provider named
33545     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
33546     */
33547     uiProviders : {},
33548
33549     /**
33550     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
33551     * child nodes before loading.
33552     */
33553     clearOnLoad : true,
33554
33555     /**
33556     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
33557     * property on loading, rather than expecting an array. (eg. more compatible to a standard
33558     * Grid query { data : [ .....] }
33559     */
33560     
33561     root : false,
33562      /**
33563     * @cfg {String} queryParam (optional) 
33564     * Name of the query as it will be passed on the querystring (defaults to 'node')
33565     * eg. the request will be ?node=[id]
33566     */
33567     
33568     
33569     queryParam: false,
33570     
33571     /**
33572      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
33573      * This is called automatically when a node is expanded, but may be used to reload
33574      * a node (or append new children if the {@link #clearOnLoad} option is false.)
33575      * @param {Roo.tree.TreeNode} node
33576      * @param {Function} callback
33577      */
33578     load : function(node, callback){
33579         if(this.clearOnLoad){
33580             while(node.firstChild){
33581                 node.removeChild(node.firstChild);
33582             }
33583         }
33584         if(node.attributes.children){ // preloaded json children
33585             var cs = node.attributes.children;
33586             for(var i = 0, len = cs.length; i < len; i++){
33587                 node.appendChild(this.createNode(cs[i]));
33588             }
33589             if(typeof callback == "function"){
33590                 callback();
33591             }
33592         }else if(this.dataUrl){
33593             this.requestData(node, callback);
33594         }
33595     },
33596
33597     getParams: function(node){
33598         var buf = [], bp = this.baseParams;
33599         for(var key in bp){
33600             if(typeof bp[key] != "function"){
33601                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33602             }
33603         }
33604         var n = this.queryParam === false ? 'node' : this.queryParam;
33605         buf.push(n + "=", encodeURIComponent(node.id));
33606         return buf.join("");
33607     },
33608
33609     requestData : function(node, callback){
33610         if(this.fireEvent("beforeload", this, node, callback) !== false){
33611             this.transId = Roo.Ajax.request({
33612                 method:this.requestMethod,
33613                 url: this.dataUrl||this.url,
33614                 success: this.handleResponse,
33615                 failure: this.handleFailure,
33616                 scope: this,
33617                 argument: {callback: callback, node: node},
33618                 params: this.getParams(node)
33619             });
33620         }else{
33621             // if the load is cancelled, make sure we notify
33622             // the node that we are done
33623             if(typeof callback == "function"){
33624                 callback();
33625             }
33626         }
33627     },
33628
33629     isLoading : function(){
33630         return this.transId ? true : false;
33631     },
33632
33633     abort : function(){
33634         if(this.isLoading()){
33635             Roo.Ajax.abort(this.transId);
33636         }
33637     },
33638
33639     // private
33640     createNode : function(attr)
33641     {
33642         // apply baseAttrs, nice idea Corey!
33643         if(this.baseAttrs){
33644             Roo.applyIf(attr, this.baseAttrs);
33645         }
33646         if(this.applyLoader !== false){
33647             attr.loader = this;
33648         }
33649         // uiProvider = depreciated..
33650         
33651         if(typeof(attr.uiProvider) == 'string'){
33652            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33653                 /**  eval:var:attr */ eval(attr.uiProvider);
33654         }
33655         if(typeof(this.uiProviders['default']) != 'undefined') {
33656             attr.uiProvider = this.uiProviders['default'];
33657         }
33658         
33659         this.fireEvent('create', this, attr);
33660         
33661         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33662         return(attr.leaf ?
33663                         new Roo.tree.TreeNode(attr) :
33664                         new Roo.tree.AsyncTreeNode(attr));
33665     },
33666
33667     processResponse : function(response, node, callback)
33668     {
33669         var json = response.responseText;
33670         try {
33671             
33672             var o = Roo.decode(json);
33673             
33674             if (this.root === false && typeof(o.success) != undefined) {
33675                 this.root = 'data'; // the default behaviour for list like data..
33676                 }
33677                 
33678             if (this.root !== false &&  !o.success) {
33679                 // it's a failure condition.
33680                 var a = response.argument;
33681                 this.fireEvent("loadexception", this, a.node, response);
33682                 Roo.log("Load failed - should have a handler really");
33683                 return;
33684             }
33685             
33686             
33687             
33688             if (this.root !== false) {
33689                  o = o[this.root];
33690             }
33691             
33692             for(var i = 0, len = o.length; i < len; i++){
33693                 var n = this.createNode(o[i]);
33694                 if(n){
33695                     node.appendChild(n);
33696                 }
33697             }
33698             if(typeof callback == "function"){
33699                 callback(this, node);
33700             }
33701         }catch(e){
33702             this.handleFailure(response);
33703         }
33704     },
33705
33706     handleResponse : function(response){
33707         this.transId = false;
33708         var a = response.argument;
33709         this.processResponse(response, a.node, a.callback);
33710         this.fireEvent("load", this, a.node, response);
33711     },
33712
33713     handleFailure : function(response)
33714     {
33715         // should handle failure better..
33716         this.transId = false;
33717         var a = response.argument;
33718         this.fireEvent("loadexception", this, a.node, response);
33719         if(typeof a.callback == "function"){
33720             a.callback(this, a.node);
33721         }
33722     }
33723 });/*
33724  * Based on:
33725  * Ext JS Library 1.1.1
33726  * Copyright(c) 2006-2007, Ext JS, LLC.
33727  *
33728  * Originally Released Under LGPL - original licence link has changed is not relivant.
33729  *
33730  * Fork - LGPL
33731  * <script type="text/javascript">
33732  */
33733
33734 /**
33735 * @class Roo.tree.TreeFilter
33736 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33737 * @param {TreePanel} tree
33738 * @param {Object} config (optional)
33739  */
33740 Roo.tree.TreeFilter = function(tree, config){
33741     this.tree = tree;
33742     this.filtered = {};
33743     Roo.apply(this, config);
33744 };
33745
33746 Roo.tree.TreeFilter.prototype = {
33747     clearBlank:false,
33748     reverse:false,
33749     autoClear:false,
33750     remove:false,
33751
33752      /**
33753      * Filter the data by a specific attribute.
33754      * @param {String/RegExp} value Either string that the attribute value
33755      * should start with or a RegExp to test against the attribute
33756      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33757      * @param {TreeNode} startNode (optional) The node to start the filter at.
33758      */
33759     filter : function(value, attr, startNode){
33760         attr = attr || "text";
33761         var f;
33762         if(typeof value == "string"){
33763             var vlen = value.length;
33764             // auto clear empty filter
33765             if(vlen == 0 && this.clearBlank){
33766                 this.clear();
33767                 return;
33768             }
33769             value = value.toLowerCase();
33770             f = function(n){
33771                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33772             };
33773         }else if(value.exec){ // regex?
33774             f = function(n){
33775                 return value.test(n.attributes[attr]);
33776             };
33777         }else{
33778             throw 'Illegal filter type, must be string or regex';
33779         }
33780         this.filterBy(f, null, startNode);
33781         },
33782
33783     /**
33784      * Filter by a function. The passed function will be called with each
33785      * node in the tree (or from the startNode). If the function returns true, the node is kept
33786      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33787      * @param {Function} fn The filter function
33788      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33789      */
33790     filterBy : function(fn, scope, startNode){
33791         startNode = startNode || this.tree.root;
33792         if(this.autoClear){
33793             this.clear();
33794         }
33795         var af = this.filtered, rv = this.reverse;
33796         var f = function(n){
33797             if(n == startNode){
33798                 return true;
33799             }
33800             if(af[n.id]){
33801                 return false;
33802             }
33803             var m = fn.call(scope || n, n);
33804             if(!m || rv){
33805                 af[n.id] = n;
33806                 n.ui.hide();
33807                 return false;
33808             }
33809             return true;
33810         };
33811         startNode.cascade(f);
33812         if(this.remove){
33813            for(var id in af){
33814                if(typeof id != "function"){
33815                    var n = af[id];
33816                    if(n && n.parentNode){
33817                        n.parentNode.removeChild(n);
33818                    }
33819                }
33820            }
33821         }
33822     },
33823
33824     /**
33825      * Clears the current filter. Note: with the "remove" option
33826      * set a filter cannot be cleared.
33827      */
33828     clear : function(){
33829         var t = this.tree;
33830         var af = this.filtered;
33831         for(var id in af){
33832             if(typeof id != "function"){
33833                 var n = af[id];
33834                 if(n){
33835                     n.ui.show();
33836                 }
33837             }
33838         }
33839         this.filtered = {};
33840     }
33841 };
33842 /*
33843  * Based on:
33844  * Ext JS Library 1.1.1
33845  * Copyright(c) 2006-2007, Ext JS, LLC.
33846  *
33847  * Originally Released Under LGPL - original licence link has changed is not relivant.
33848  *
33849  * Fork - LGPL
33850  * <script type="text/javascript">
33851  */
33852  
33853
33854 /**
33855  * @class Roo.tree.TreeSorter
33856  * Provides sorting of nodes in a TreePanel
33857  * 
33858  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33859  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33860  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33861  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33862  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33863  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33864  * @constructor
33865  * @param {TreePanel} tree
33866  * @param {Object} config
33867  */
33868 Roo.tree.TreeSorter = function(tree, config){
33869     Roo.apply(this, config);
33870     tree.on("beforechildrenrendered", this.doSort, this);
33871     tree.on("append", this.updateSort, this);
33872     tree.on("insert", this.updateSort, this);
33873     
33874     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33875     var p = this.property || "text";
33876     var sortType = this.sortType;
33877     var fs = this.folderSort;
33878     var cs = this.caseSensitive === true;
33879     var leafAttr = this.leafAttr || 'leaf';
33880
33881     this.sortFn = function(n1, n2){
33882         if(fs){
33883             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33884                 return 1;
33885             }
33886             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33887                 return -1;
33888             }
33889         }
33890         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33891         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33892         if(v1 < v2){
33893                         return dsc ? +1 : -1;
33894                 }else if(v1 > v2){
33895                         return dsc ? -1 : +1;
33896         }else{
33897                 return 0;
33898         }
33899     };
33900 };
33901
33902 Roo.tree.TreeSorter.prototype = {
33903     doSort : function(node){
33904         node.sort(this.sortFn);
33905     },
33906     
33907     compareNodes : function(n1, n2){
33908         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33909     },
33910     
33911     updateSort : function(tree, node){
33912         if(node.childrenRendered){
33913             this.doSort.defer(1, this, [node]);
33914         }
33915     }
33916 };/*
33917  * Based on:
33918  * Ext JS Library 1.1.1
33919  * Copyright(c) 2006-2007, Ext JS, LLC.
33920  *
33921  * Originally Released Under LGPL - original licence link has changed is not relivant.
33922  *
33923  * Fork - LGPL
33924  * <script type="text/javascript">
33925  */
33926
33927 if(Roo.dd.DropZone){
33928     
33929 Roo.tree.TreeDropZone = function(tree, config){
33930     this.allowParentInsert = false;
33931     this.allowContainerDrop = false;
33932     this.appendOnly = false;
33933     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33934     this.tree = tree;
33935     this.lastInsertClass = "x-tree-no-status";
33936     this.dragOverData = {};
33937 };
33938
33939 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33940     ddGroup : "TreeDD",
33941     scroll:  true,
33942     
33943     expandDelay : 1000,
33944     
33945     expandNode : function(node){
33946         if(node.hasChildNodes() && !node.isExpanded()){
33947             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33948         }
33949     },
33950     
33951     queueExpand : function(node){
33952         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33953     },
33954     
33955     cancelExpand : function(){
33956         if(this.expandProcId){
33957             clearTimeout(this.expandProcId);
33958             this.expandProcId = false;
33959         }
33960     },
33961     
33962     isValidDropPoint : function(n, pt, dd, e, data){
33963         if(!n || !data){ return false; }
33964         var targetNode = n.node;
33965         var dropNode = data.node;
33966         // default drop rules
33967         if(!(targetNode && targetNode.isTarget && pt)){
33968             return false;
33969         }
33970         if(pt == "append" && targetNode.allowChildren === false){
33971             return false;
33972         }
33973         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33974             return false;
33975         }
33976         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33977             return false;
33978         }
33979         // reuse the object
33980         var overEvent = this.dragOverData;
33981         overEvent.tree = this.tree;
33982         overEvent.target = targetNode;
33983         overEvent.data = data;
33984         overEvent.point = pt;
33985         overEvent.source = dd;
33986         overEvent.rawEvent = e;
33987         overEvent.dropNode = dropNode;
33988         overEvent.cancel = false;  
33989         var result = this.tree.fireEvent("nodedragover", overEvent);
33990         return overEvent.cancel === false && result !== false;
33991     },
33992     
33993     getDropPoint : function(e, n, dd)
33994     {
33995         var tn = n.node;
33996         if(tn.isRoot){
33997             return tn.allowChildren !== false ? "append" : false; // always append for root
33998         }
33999         var dragEl = n.ddel;
34000         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34001         var y = Roo.lib.Event.getPageY(e);
34002         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34003         
34004         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34005         var noAppend = tn.allowChildren === false;
34006         if(this.appendOnly || tn.parentNode.allowChildren === false){
34007             return noAppend ? false : "append";
34008         }
34009         var noBelow = false;
34010         if(!this.allowParentInsert){
34011             noBelow = tn.hasChildNodes() && tn.isExpanded();
34012         }
34013         var q = (b - t) / (noAppend ? 2 : 3);
34014         if(y >= t && y < (t + q)){
34015             return "above";
34016         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34017             return "below";
34018         }else{
34019             return "append";
34020         }
34021     },
34022     
34023     onNodeEnter : function(n, dd, e, data)
34024     {
34025         this.cancelExpand();
34026     },
34027     
34028     onNodeOver : function(n, dd, e, data)
34029     {
34030        
34031         var pt = this.getDropPoint(e, n, dd);
34032         var node = n.node;
34033         
34034         // auto node expand check
34035         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34036             this.queueExpand(node);
34037         }else if(pt != "append"){
34038             this.cancelExpand();
34039         }
34040         
34041         // set the insert point style on the target node
34042         var returnCls = this.dropNotAllowed;
34043         if(this.isValidDropPoint(n, pt, dd, e, data)){
34044            if(pt){
34045                var el = n.ddel;
34046                var cls;
34047                if(pt == "above"){
34048                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34049                    cls = "x-tree-drag-insert-above";
34050                }else if(pt == "below"){
34051                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34052                    cls = "x-tree-drag-insert-below";
34053                }else{
34054                    returnCls = "x-tree-drop-ok-append";
34055                    cls = "x-tree-drag-append";
34056                }
34057                if(this.lastInsertClass != cls){
34058                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34059                    this.lastInsertClass = cls;
34060                }
34061            }
34062        }
34063        return returnCls;
34064     },
34065     
34066     onNodeOut : function(n, dd, e, data){
34067         
34068         this.cancelExpand();
34069         this.removeDropIndicators(n);
34070     },
34071     
34072     onNodeDrop : function(n, dd, e, data){
34073         var point = this.getDropPoint(e, n, dd);
34074         var targetNode = n.node;
34075         targetNode.ui.startDrop();
34076         if(!this.isValidDropPoint(n, point, dd, e, data)){
34077             targetNode.ui.endDrop();
34078             return false;
34079         }
34080         // first try to find the drop node
34081         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34082         var dropEvent = {
34083             tree : this.tree,
34084             target: targetNode,
34085             data: data,
34086             point: point,
34087             source: dd,
34088             rawEvent: e,
34089             dropNode: dropNode,
34090             cancel: !dropNode   
34091         };
34092         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34093         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34094             targetNode.ui.endDrop();
34095             return false;
34096         }
34097         // allow target changing
34098         targetNode = dropEvent.target;
34099         if(point == "append" && !targetNode.isExpanded()){
34100             targetNode.expand(false, null, function(){
34101                 this.completeDrop(dropEvent);
34102             }.createDelegate(this));
34103         }else{
34104             this.completeDrop(dropEvent);
34105         }
34106         return true;
34107     },
34108     
34109     completeDrop : function(de){
34110         var ns = de.dropNode, p = de.point, t = de.target;
34111         if(!(ns instanceof Array)){
34112             ns = [ns];
34113         }
34114         var n;
34115         for(var i = 0, len = ns.length; i < len; i++){
34116             n = ns[i];
34117             if(p == "above"){
34118                 t.parentNode.insertBefore(n, t);
34119             }else if(p == "below"){
34120                 t.parentNode.insertBefore(n, t.nextSibling);
34121             }else{
34122                 t.appendChild(n);
34123             }
34124         }
34125         n.ui.focus();
34126         if(this.tree.hlDrop){
34127             n.ui.highlight();
34128         }
34129         t.ui.endDrop();
34130         this.tree.fireEvent("nodedrop", de);
34131     },
34132     
34133     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34134         if(this.tree.hlDrop){
34135             dropNode.ui.focus();
34136             dropNode.ui.highlight();
34137         }
34138         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34139     },
34140     
34141     getTree : function(){
34142         return this.tree;
34143     },
34144     
34145     removeDropIndicators : function(n){
34146         if(n && n.ddel){
34147             var el = n.ddel;
34148             Roo.fly(el).removeClass([
34149                     "x-tree-drag-insert-above",
34150                     "x-tree-drag-insert-below",
34151                     "x-tree-drag-append"]);
34152             this.lastInsertClass = "_noclass";
34153         }
34154     },
34155     
34156     beforeDragDrop : function(target, e, id){
34157         this.cancelExpand();
34158         return true;
34159     },
34160     
34161     afterRepair : function(data){
34162         if(data && Roo.enableFx){
34163             data.node.ui.highlight();
34164         }
34165         this.hideProxy();
34166     } 
34167     
34168 });
34169
34170 }
34171 /*
34172  * Based on:
34173  * Ext JS Library 1.1.1
34174  * Copyright(c) 2006-2007, Ext JS, LLC.
34175  *
34176  * Originally Released Under LGPL - original licence link has changed is not relivant.
34177  *
34178  * Fork - LGPL
34179  * <script type="text/javascript">
34180  */
34181  
34182
34183 if(Roo.dd.DragZone){
34184 Roo.tree.TreeDragZone = function(tree, config){
34185     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34186     this.tree = tree;
34187 };
34188
34189 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34190     ddGroup : "TreeDD",
34191    
34192     onBeforeDrag : function(data, e){
34193         var n = data.node;
34194         return n && n.draggable && !n.disabled;
34195     },
34196      
34197     
34198     onInitDrag : function(e){
34199         var data = this.dragData;
34200         this.tree.getSelectionModel().select(data.node);
34201         this.proxy.update("");
34202         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34203         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34204     },
34205     
34206     getRepairXY : function(e, data){
34207         return data.node.ui.getDDRepairXY();
34208     },
34209     
34210     onEndDrag : function(data, e){
34211         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34212         
34213         
34214     },
34215     
34216     onValidDrop : function(dd, e, id){
34217         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34218         this.hideProxy();
34219     },
34220     
34221     beforeInvalidDrop : function(e, id){
34222         // this scrolls the original position back into view
34223         var sm = this.tree.getSelectionModel();
34224         sm.clearSelections();
34225         sm.select(this.dragData.node);
34226     }
34227 });
34228 }/*
34229  * Based on:
34230  * Ext JS Library 1.1.1
34231  * Copyright(c) 2006-2007, Ext JS, LLC.
34232  *
34233  * Originally Released Under LGPL - original licence link has changed is not relivant.
34234  *
34235  * Fork - LGPL
34236  * <script type="text/javascript">
34237  */
34238 /**
34239  * @class Roo.tree.TreeEditor
34240  * @extends Roo.Editor
34241  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34242  * as the editor field.
34243  * @constructor
34244  * @param {Object} config (used to be the tree panel.)
34245  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34246  * 
34247  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34248  * @cfg {Roo.form.TextField|Object} field The field configuration
34249  *
34250  * 
34251  */
34252 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34253     var tree = config;
34254     var field;
34255     if (oldconfig) { // old style..
34256         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34257     } else {
34258         // new style..
34259         tree = config.tree;
34260         config.field = config.field  || {};
34261         config.field.xtype = 'TextField';
34262         field = Roo.factory(config.field, Roo.form);
34263     }
34264     config = config || {};
34265     
34266     
34267     this.addEvents({
34268         /**
34269          * @event beforenodeedit
34270          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34271          * false from the handler of this event.
34272          * @param {Editor} this
34273          * @param {Roo.tree.Node} node 
34274          */
34275         "beforenodeedit" : true
34276     });
34277     
34278     //Roo.log(config);
34279     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34280
34281     this.tree = tree;
34282
34283     tree.on('beforeclick', this.beforeNodeClick, this);
34284     tree.getTreeEl().on('mousedown', this.hide, this);
34285     this.on('complete', this.updateNode, this);
34286     this.on('beforestartedit', this.fitToTree, this);
34287     this.on('startedit', this.bindScroll, this, {delay:10});
34288     this.on('specialkey', this.onSpecialKey, this);
34289 };
34290
34291 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34292     /**
34293      * @cfg {String} alignment
34294      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34295      */
34296     alignment: "l-l",
34297     // inherit
34298     autoSize: false,
34299     /**
34300      * @cfg {Boolean} hideEl
34301      * True to hide the bound element while the editor is displayed (defaults to false)
34302      */
34303     hideEl : false,
34304     /**
34305      * @cfg {String} cls
34306      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34307      */
34308     cls: "x-small-editor x-tree-editor",
34309     /**
34310      * @cfg {Boolean} shim
34311      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34312      */
34313     shim:false,
34314     // inherit
34315     shadow:"frame",
34316     /**
34317      * @cfg {Number} maxWidth
34318      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34319      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34320      * scroll and client offsets into account prior to each edit.
34321      */
34322     maxWidth: 250,
34323
34324     editDelay : 350,
34325
34326     // private
34327     fitToTree : function(ed, el){
34328         var td = this.tree.getTreeEl().dom, nd = el.dom;
34329         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34330             td.scrollLeft = nd.offsetLeft;
34331         }
34332         var w = Math.min(
34333                 this.maxWidth,
34334                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
34335         this.setSize(w, '');
34336         
34337         return this.fireEvent('beforenodeedit', this, this.editNode);
34338         
34339     },
34340
34341     // private
34342     triggerEdit : function(node){
34343         this.completeEdit();
34344         this.editNode = node;
34345         this.startEdit(node.ui.textNode, node.text);
34346     },
34347
34348     // private
34349     bindScroll : function(){
34350         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
34351     },
34352
34353     // private
34354     beforeNodeClick : function(node, e){
34355         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
34356         this.lastClick = new Date();
34357         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
34358             e.stopEvent();
34359             this.triggerEdit(node);
34360             return false;
34361         }
34362         return true;
34363     },
34364
34365     // private
34366     updateNode : function(ed, value){
34367         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
34368         this.editNode.setText(value);
34369     },
34370
34371     // private
34372     onHide : function(){
34373         Roo.tree.TreeEditor.superclass.onHide.call(this);
34374         if(this.editNode){
34375             this.editNode.ui.focus();
34376         }
34377     },
34378
34379     // private
34380     onSpecialKey : function(field, e){
34381         var k = e.getKey();
34382         if(k == e.ESC){
34383             e.stopEvent();
34384             this.cancelEdit();
34385         }else if(k == e.ENTER && !e.hasModifier()){
34386             e.stopEvent();
34387             this.completeEdit();
34388         }
34389     }
34390 });//<Script type="text/javascript">
34391 /*
34392  * Based on:
34393  * Ext JS Library 1.1.1
34394  * Copyright(c) 2006-2007, Ext JS, LLC.
34395  *
34396  * Originally Released Under LGPL - original licence link has changed is not relivant.
34397  *
34398  * Fork - LGPL
34399  * <script type="text/javascript">
34400  */
34401  
34402 /**
34403  * Not documented??? - probably should be...
34404  */
34405
34406 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
34407     //focus: Roo.emptyFn, // prevent odd scrolling behavior
34408     
34409     renderElements : function(n, a, targetNode, bulkRender){
34410         //consel.log("renderElements?");
34411         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34412
34413         var t = n.getOwnerTree();
34414         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
34415         
34416         var cols = t.columns;
34417         var bw = t.borderWidth;
34418         var c = cols[0];
34419         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34420          var cb = typeof a.checked == "boolean";
34421         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34422         var colcls = 'x-t-' + tid + '-c0';
34423         var buf = [
34424             '<li class="x-tree-node">',
34425             
34426                 
34427                 '<div class="x-tree-node-el ', a.cls,'">',
34428                     // extran...
34429                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
34430                 
34431                 
34432                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
34433                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
34434                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
34435                            (a.icon ? ' x-tree-node-inline-icon' : ''),
34436                            (a.iconCls ? ' '+a.iconCls : ''),
34437                            '" unselectable="on" />',
34438                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
34439                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
34440                              
34441                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34442                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
34443                             '<span unselectable="on" qtip="' + tx + '">',
34444                              tx,
34445                              '</span></a>' ,
34446                     '</div>',
34447                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34448                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
34449                  ];
34450         for(var i = 1, len = cols.length; i < len; i++){
34451             c = cols[i];
34452             colcls = 'x-t-' + tid + '-c' +i;
34453             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34454             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
34455                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
34456                       "</div>");
34457          }
34458          
34459          buf.push(
34460             '</a>',
34461             '<div class="x-clear"></div></div>',
34462             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34463             "</li>");
34464         
34465         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34466             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34467                                 n.nextSibling.ui.getEl(), buf.join(""));
34468         }else{
34469             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34470         }
34471         var el = this.wrap.firstChild;
34472         this.elRow = el;
34473         this.elNode = el.firstChild;
34474         this.ranchor = el.childNodes[1];
34475         this.ctNode = this.wrap.childNodes[1];
34476         var cs = el.firstChild.childNodes;
34477         this.indentNode = cs[0];
34478         this.ecNode = cs[1];
34479         this.iconNode = cs[2];
34480         var index = 3;
34481         if(cb){
34482             this.checkbox = cs[3];
34483             index++;
34484         }
34485         this.anchor = cs[index];
34486         
34487         this.textNode = cs[index].firstChild;
34488         
34489         //el.on("click", this.onClick, this);
34490         //el.on("dblclick", this.onDblClick, this);
34491         
34492         
34493        // console.log(this);
34494     },
34495     initEvents : function(){
34496         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
34497         
34498             
34499         var a = this.ranchor;
34500
34501         var el = Roo.get(a);
34502
34503         if(Roo.isOpera){ // opera render bug ignores the CSS
34504             el.setStyle("text-decoration", "none");
34505         }
34506
34507         el.on("click", this.onClick, this);
34508         el.on("dblclick", this.onDblClick, this);
34509         el.on("contextmenu", this.onContextMenu, this);
34510         
34511     },
34512     
34513     /*onSelectedChange : function(state){
34514         if(state){
34515             this.focus();
34516             this.addClass("x-tree-selected");
34517         }else{
34518             //this.blur();
34519             this.removeClass("x-tree-selected");
34520         }
34521     },*/
34522     addClass : function(cls){
34523         if(this.elRow){
34524             Roo.fly(this.elRow).addClass(cls);
34525         }
34526         
34527     },
34528     
34529     
34530     removeClass : function(cls){
34531         if(this.elRow){
34532             Roo.fly(this.elRow).removeClass(cls);
34533         }
34534     }
34535
34536     
34537     
34538 });//<Script type="text/javascript">
34539
34540 /*
34541  * Based on:
34542  * Ext JS Library 1.1.1
34543  * Copyright(c) 2006-2007, Ext JS, LLC.
34544  *
34545  * Originally Released Under LGPL - original licence link has changed is not relivant.
34546  *
34547  * Fork - LGPL
34548  * <script type="text/javascript">
34549  */
34550  
34551
34552 /**
34553  * @class Roo.tree.ColumnTree
34554  * @extends Roo.data.TreePanel
34555  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
34556  * @cfg {int} borderWidth  compined right/left border allowance
34557  * @constructor
34558  * @param {String/HTMLElement/Element} el The container element
34559  * @param {Object} config
34560  */
34561 Roo.tree.ColumnTree =  function(el, config)
34562 {
34563    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
34564    this.addEvents({
34565         /**
34566         * @event resize
34567         * Fire this event on a container when it resizes
34568         * @param {int} w Width
34569         * @param {int} h Height
34570         */
34571        "resize" : true
34572     });
34573     this.on('resize', this.onResize, this);
34574 };
34575
34576 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
34577     //lines:false,
34578     
34579     
34580     borderWidth: Roo.isBorderBox ? 0 : 2, 
34581     headEls : false,
34582     
34583     render : function(){
34584         // add the header.....
34585        
34586         Roo.tree.ColumnTree.superclass.render.apply(this);
34587         
34588         this.el.addClass('x-column-tree');
34589         
34590         this.headers = this.el.createChild(
34591             {cls:'x-tree-headers'},this.innerCt.dom);
34592    
34593         var cols = this.columns, c;
34594         var totalWidth = 0;
34595         this.headEls = [];
34596         var  len = cols.length;
34597         for(var i = 0; i < len; i++){
34598              c = cols[i];
34599              totalWidth += c.width;
34600             this.headEls.push(this.headers.createChild({
34601                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
34602                  cn: {
34603                      cls:'x-tree-hd-text',
34604                      html: c.header
34605                  },
34606                  style:'width:'+(c.width-this.borderWidth)+'px;'
34607              }));
34608         }
34609         this.headers.createChild({cls:'x-clear'});
34610         // prevent floats from wrapping when clipped
34611         this.headers.setWidth(totalWidth);
34612         //this.innerCt.setWidth(totalWidth);
34613         this.innerCt.setStyle({ overflow: 'auto' });
34614         this.onResize(this.width, this.height);
34615              
34616         
34617     },
34618     onResize : function(w,h)
34619     {
34620         this.height = h;
34621         this.width = w;
34622         // resize cols..
34623         this.innerCt.setWidth(this.width);
34624         this.innerCt.setHeight(this.height-20);
34625         
34626         // headers...
34627         var cols = this.columns, c;
34628         var totalWidth = 0;
34629         var expEl = false;
34630         var len = cols.length;
34631         for(var i = 0; i < len; i++){
34632             c = cols[i];
34633             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34634                 // it's the expander..
34635                 expEl  = this.headEls[i];
34636                 continue;
34637             }
34638             totalWidth += c.width;
34639             
34640         }
34641         if (expEl) {
34642             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34643         }
34644         this.headers.setWidth(w-20);
34645
34646         
34647         
34648         
34649     }
34650 });
34651 /*
34652  * Based on:
34653  * Ext JS Library 1.1.1
34654  * Copyright(c) 2006-2007, Ext JS, LLC.
34655  *
34656  * Originally Released Under LGPL - original licence link has changed is not relivant.
34657  *
34658  * Fork - LGPL
34659  * <script type="text/javascript">
34660  */
34661  
34662 /**
34663  * @class Roo.menu.Menu
34664  * @extends Roo.util.Observable
34665  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34666  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34667  * @constructor
34668  * Creates a new Menu
34669  * @param {Object} config Configuration options
34670  */
34671 Roo.menu.Menu = function(config){
34672     Roo.apply(this, config);
34673     this.id = this.id || Roo.id();
34674     this.addEvents({
34675         /**
34676          * @event beforeshow
34677          * Fires before this menu is displayed
34678          * @param {Roo.menu.Menu} this
34679          */
34680         beforeshow : true,
34681         /**
34682          * @event beforehide
34683          * Fires before this menu is hidden
34684          * @param {Roo.menu.Menu} this
34685          */
34686         beforehide : true,
34687         /**
34688          * @event show
34689          * Fires after this menu is displayed
34690          * @param {Roo.menu.Menu} this
34691          */
34692         show : true,
34693         /**
34694          * @event hide
34695          * Fires after this menu is hidden
34696          * @param {Roo.menu.Menu} this
34697          */
34698         hide : true,
34699         /**
34700          * @event click
34701          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34702          * @param {Roo.menu.Menu} this
34703          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34704          * @param {Roo.EventObject} e
34705          */
34706         click : true,
34707         /**
34708          * @event mouseover
34709          * Fires when the mouse is hovering over this menu
34710          * @param {Roo.menu.Menu} this
34711          * @param {Roo.EventObject} e
34712          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34713          */
34714         mouseover : true,
34715         /**
34716          * @event mouseout
34717          * Fires when the mouse exits this menu
34718          * @param {Roo.menu.Menu} this
34719          * @param {Roo.EventObject} e
34720          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34721          */
34722         mouseout : true,
34723         /**
34724          * @event itemclick
34725          * Fires when a menu item contained in this menu is clicked
34726          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34727          * @param {Roo.EventObject} e
34728          */
34729         itemclick: true
34730     });
34731     if (this.registerMenu) {
34732         Roo.menu.MenuMgr.register(this);
34733     }
34734     
34735     var mis = this.items;
34736     this.items = new Roo.util.MixedCollection();
34737     if(mis){
34738         this.add.apply(this, mis);
34739     }
34740 };
34741
34742 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34743     /**
34744      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34745      */
34746     minWidth : 120,
34747     /**
34748      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34749      * for bottom-right shadow (defaults to "sides")
34750      */
34751     shadow : "sides",
34752     /**
34753      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34754      * this menu (defaults to "tl-tr?")
34755      */
34756     subMenuAlign : "tl-tr?",
34757     /**
34758      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34759      * relative to its element of origin (defaults to "tl-bl?")
34760      */
34761     defaultAlign : "tl-bl?",
34762     /**
34763      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34764      */
34765     allowOtherMenus : false,
34766     /**
34767      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34768      */
34769     registerMenu : true,
34770
34771     hidden:true,
34772
34773     // private
34774     render : function(){
34775         if(this.el){
34776             return;
34777         }
34778         var el = this.el = new Roo.Layer({
34779             cls: "x-menu",
34780             shadow:this.shadow,
34781             constrain: false,
34782             parentEl: this.parentEl || document.body,
34783             zindex:15000
34784         });
34785
34786         this.keyNav = new Roo.menu.MenuNav(this);
34787
34788         if(this.plain){
34789             el.addClass("x-menu-plain");
34790         }
34791         if(this.cls){
34792             el.addClass(this.cls);
34793         }
34794         // generic focus element
34795         this.focusEl = el.createChild({
34796             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34797         });
34798         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34799         ul.on("click", this.onClick, this);
34800         ul.on("mouseover", this.onMouseOver, this);
34801         ul.on("mouseout", this.onMouseOut, this);
34802         this.items.each(function(item){
34803             if (item.hidden) {
34804                 return;
34805             }
34806             
34807             var li = document.createElement("li");
34808             li.className = "x-menu-list-item";
34809             ul.dom.appendChild(li);
34810             item.render(li, this);
34811         }, this);
34812         this.ul = ul;
34813         this.autoWidth();
34814     },
34815
34816     // private
34817     autoWidth : function(){
34818         var el = this.el, ul = this.ul;
34819         if(!el){
34820             return;
34821         }
34822         var w = this.width;
34823         if(w){
34824             el.setWidth(w);
34825         }else if(Roo.isIE){
34826             el.setWidth(this.minWidth);
34827             var t = el.dom.offsetWidth; // force recalc
34828             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34829         }
34830     },
34831
34832     // private
34833     delayAutoWidth : function(){
34834         if(this.rendered){
34835             if(!this.awTask){
34836                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34837             }
34838             this.awTask.delay(20);
34839         }
34840     },
34841
34842     // private
34843     findTargetItem : function(e){
34844         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34845         if(t && t.menuItemId){
34846             return this.items.get(t.menuItemId);
34847         }
34848     },
34849
34850     // private
34851     onClick : function(e){
34852         var t;
34853         if(t = this.findTargetItem(e)){
34854             t.onClick(e);
34855             this.fireEvent("click", this, t, e);
34856         }
34857     },
34858
34859     // private
34860     setActiveItem : function(item, autoExpand){
34861         if(item != this.activeItem){
34862             if(this.activeItem){
34863                 this.activeItem.deactivate();
34864             }
34865             this.activeItem = item;
34866             item.activate(autoExpand);
34867         }else if(autoExpand){
34868             item.expandMenu();
34869         }
34870     },
34871
34872     // private
34873     tryActivate : function(start, step){
34874         var items = this.items;
34875         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34876             var item = items.get(i);
34877             if(!item.disabled && item.canActivate){
34878                 this.setActiveItem(item, false);
34879                 return item;
34880             }
34881         }
34882         return false;
34883     },
34884
34885     // private
34886     onMouseOver : function(e){
34887         var t;
34888         if(t = this.findTargetItem(e)){
34889             if(t.canActivate && !t.disabled){
34890                 this.setActiveItem(t, true);
34891             }
34892         }
34893         this.fireEvent("mouseover", this, e, t);
34894     },
34895
34896     // private
34897     onMouseOut : function(e){
34898         var t;
34899         if(t = this.findTargetItem(e)){
34900             if(t == this.activeItem && t.shouldDeactivate(e)){
34901                 this.activeItem.deactivate();
34902                 delete this.activeItem;
34903             }
34904         }
34905         this.fireEvent("mouseout", this, e, t);
34906     },
34907
34908     /**
34909      * Read-only.  Returns true if the menu is currently displayed, else false.
34910      * @type Boolean
34911      */
34912     isVisible : function(){
34913         return this.el && !this.hidden;
34914     },
34915
34916     /**
34917      * Displays this menu relative to another element
34918      * @param {String/HTMLElement/Roo.Element} element The element to align to
34919      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34920      * the element (defaults to this.defaultAlign)
34921      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34922      */
34923     show : function(el, pos, parentMenu){
34924         this.parentMenu = parentMenu;
34925         if(!this.el){
34926             this.render();
34927         }
34928         this.fireEvent("beforeshow", this);
34929         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34930     },
34931
34932     /**
34933      * Displays this menu at a specific xy position
34934      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34935      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34936      */
34937     showAt : function(xy, parentMenu, /* private: */_e){
34938         this.parentMenu = parentMenu;
34939         if(!this.el){
34940             this.render();
34941         }
34942         if(_e !== false){
34943             this.fireEvent("beforeshow", this);
34944             xy = this.el.adjustForConstraints(xy);
34945         }
34946         this.el.setXY(xy);
34947         this.el.show();
34948         this.hidden = false;
34949         this.focus();
34950         this.fireEvent("show", this);
34951     },
34952
34953     focus : function(){
34954         if(!this.hidden){
34955             this.doFocus.defer(50, this);
34956         }
34957     },
34958
34959     doFocus : function(){
34960         if(!this.hidden){
34961             this.focusEl.focus();
34962         }
34963     },
34964
34965     /**
34966      * Hides this menu and optionally all parent menus
34967      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34968      */
34969     hide : function(deep){
34970         if(this.el && this.isVisible()){
34971             this.fireEvent("beforehide", this);
34972             if(this.activeItem){
34973                 this.activeItem.deactivate();
34974                 this.activeItem = null;
34975             }
34976             this.el.hide();
34977             this.hidden = true;
34978             this.fireEvent("hide", this);
34979         }
34980         if(deep === true && this.parentMenu){
34981             this.parentMenu.hide(true);
34982         }
34983     },
34984
34985     /**
34986      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34987      * Any of the following are valid:
34988      * <ul>
34989      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34990      * <li>An HTMLElement object which will be converted to a menu item</li>
34991      * <li>A menu item config object that will be created as a new menu item</li>
34992      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34993      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34994      * </ul>
34995      * Usage:
34996      * <pre><code>
34997 // Create the menu
34998 var menu = new Roo.menu.Menu();
34999
35000 // Create a menu item to add by reference
35001 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35002
35003 // Add a bunch of items at once using different methods.
35004 // Only the last item added will be returned.
35005 var item = menu.add(
35006     menuItem,                // add existing item by ref
35007     'Dynamic Item',          // new TextItem
35008     '-',                     // new separator
35009     { text: 'Config Item' }  // new item by config
35010 );
35011 </code></pre>
35012      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35013      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35014      */
35015     add : function(){
35016         var a = arguments, l = a.length, item;
35017         for(var i = 0; i < l; i++){
35018             var el = a[i];
35019             if ((typeof(el) == "object") && el.xtype && el.xns) {
35020                 el = Roo.factory(el, Roo.menu);
35021             }
35022             
35023             if(el.render){ // some kind of Item
35024                 item = this.addItem(el);
35025             }else if(typeof el == "string"){ // string
35026                 if(el == "separator" || el == "-"){
35027                     item = this.addSeparator();
35028                 }else{
35029                     item = this.addText(el);
35030                 }
35031             }else if(el.tagName || el.el){ // element
35032                 item = this.addElement(el);
35033             }else if(typeof el == "object"){ // must be menu item config?
35034                 item = this.addMenuItem(el);
35035             }
35036         }
35037         return item;
35038     },
35039
35040     /**
35041      * Returns this menu's underlying {@link Roo.Element} object
35042      * @return {Roo.Element} The element
35043      */
35044     getEl : function(){
35045         if(!this.el){
35046             this.render();
35047         }
35048         return this.el;
35049     },
35050
35051     /**
35052      * Adds a separator bar to the menu
35053      * @return {Roo.menu.Item} The menu item that was added
35054      */
35055     addSeparator : function(){
35056         return this.addItem(new Roo.menu.Separator());
35057     },
35058
35059     /**
35060      * Adds an {@link Roo.Element} object to the menu
35061      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35062      * @return {Roo.menu.Item} The menu item that was added
35063      */
35064     addElement : function(el){
35065         return this.addItem(new Roo.menu.BaseItem(el));
35066     },
35067
35068     /**
35069      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35070      * @param {Roo.menu.Item} item The menu item to add
35071      * @return {Roo.menu.Item} The menu item that was added
35072      */
35073     addItem : function(item){
35074         this.items.add(item);
35075         if(this.ul){
35076             var li = document.createElement("li");
35077             li.className = "x-menu-list-item";
35078             this.ul.dom.appendChild(li);
35079             item.render(li, this);
35080             this.delayAutoWidth();
35081         }
35082         return item;
35083     },
35084
35085     /**
35086      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35087      * @param {Object} config A MenuItem config object
35088      * @return {Roo.menu.Item} The menu item that was added
35089      */
35090     addMenuItem : function(config){
35091         if(!(config instanceof Roo.menu.Item)){
35092             if(typeof config.checked == "boolean"){ // must be check menu item config?
35093                 config = new Roo.menu.CheckItem(config);
35094             }else{
35095                 config = new Roo.menu.Item(config);
35096             }
35097         }
35098         return this.addItem(config);
35099     },
35100
35101     /**
35102      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35103      * @param {String} text The text to display in the menu item
35104      * @return {Roo.menu.Item} The menu item that was added
35105      */
35106     addText : function(text){
35107         return this.addItem(new Roo.menu.TextItem({ text : text }));
35108     },
35109
35110     /**
35111      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35112      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35113      * @param {Roo.menu.Item} item The menu item to add
35114      * @return {Roo.menu.Item} The menu item that was added
35115      */
35116     insert : function(index, item){
35117         this.items.insert(index, item);
35118         if(this.ul){
35119             var li = document.createElement("li");
35120             li.className = "x-menu-list-item";
35121             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35122             item.render(li, this);
35123             this.delayAutoWidth();
35124         }
35125         return item;
35126     },
35127
35128     /**
35129      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35130      * @param {Roo.menu.Item} item The menu item to remove
35131      */
35132     remove : function(item){
35133         this.items.removeKey(item.id);
35134         item.destroy();
35135     },
35136
35137     /**
35138      * Removes and destroys all items in the menu
35139      */
35140     removeAll : function(){
35141         var f;
35142         while(f = this.items.first()){
35143             this.remove(f);
35144         }
35145     }
35146 });
35147
35148 // MenuNav is a private utility class used internally by the Menu
35149 Roo.menu.MenuNav = function(menu){
35150     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35151     this.scope = this.menu = menu;
35152 };
35153
35154 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35155     doRelay : function(e, h){
35156         var k = e.getKey();
35157         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35158             this.menu.tryActivate(0, 1);
35159             return false;
35160         }
35161         return h.call(this.scope || this, e, this.menu);
35162     },
35163
35164     up : function(e, m){
35165         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35166             m.tryActivate(m.items.length-1, -1);
35167         }
35168     },
35169
35170     down : function(e, m){
35171         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35172             m.tryActivate(0, 1);
35173         }
35174     },
35175
35176     right : function(e, m){
35177         if(m.activeItem){
35178             m.activeItem.expandMenu(true);
35179         }
35180     },
35181
35182     left : function(e, m){
35183         m.hide();
35184         if(m.parentMenu && m.parentMenu.activeItem){
35185             m.parentMenu.activeItem.activate();
35186         }
35187     },
35188
35189     enter : function(e, m){
35190         if(m.activeItem){
35191             e.stopPropagation();
35192             m.activeItem.onClick(e);
35193             m.fireEvent("click", this, m.activeItem);
35194             return true;
35195         }
35196     }
35197 });/*
35198  * Based on:
35199  * Ext JS Library 1.1.1
35200  * Copyright(c) 2006-2007, Ext JS, LLC.
35201  *
35202  * Originally Released Under LGPL - original licence link has changed is not relivant.
35203  *
35204  * Fork - LGPL
35205  * <script type="text/javascript">
35206  */
35207  
35208 /**
35209  * @class Roo.menu.MenuMgr
35210  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35211  * @singleton
35212  */
35213 Roo.menu.MenuMgr = function(){
35214    var menus, active, groups = {}, attached = false, lastShow = new Date();
35215
35216    // private - called when first menu is created
35217    function init(){
35218        menus = {};
35219        active = new Roo.util.MixedCollection();
35220        Roo.get(document).addKeyListener(27, function(){
35221            if(active.length > 0){
35222                hideAll();
35223            }
35224        });
35225    }
35226
35227    // private
35228    function hideAll(){
35229        if(active && active.length > 0){
35230            var c = active.clone();
35231            c.each(function(m){
35232                m.hide();
35233            });
35234        }
35235    }
35236
35237    // private
35238    function onHide(m){
35239        active.remove(m);
35240        if(active.length < 1){
35241            Roo.get(document).un("mousedown", onMouseDown);
35242            attached = false;
35243        }
35244    }
35245
35246    // private
35247    function onShow(m){
35248        var last = active.last();
35249        lastShow = new Date();
35250        active.add(m);
35251        if(!attached){
35252            Roo.get(document).on("mousedown", onMouseDown);
35253            attached = true;
35254        }
35255        if(m.parentMenu){
35256           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35257           m.parentMenu.activeChild = m;
35258        }else if(last && last.isVisible()){
35259           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35260        }
35261    }
35262
35263    // private
35264    function onBeforeHide(m){
35265        if(m.activeChild){
35266            m.activeChild.hide();
35267        }
35268        if(m.autoHideTimer){
35269            clearTimeout(m.autoHideTimer);
35270            delete m.autoHideTimer;
35271        }
35272    }
35273
35274    // private
35275    function onBeforeShow(m){
35276        var pm = m.parentMenu;
35277        if(!pm && !m.allowOtherMenus){
35278            hideAll();
35279        }else if(pm && pm.activeChild && active != m){
35280            pm.activeChild.hide();
35281        }
35282    }
35283
35284    // private
35285    function onMouseDown(e){
35286        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35287            hideAll();
35288        }
35289    }
35290
35291    // private
35292    function onBeforeCheck(mi, state){
35293        if(state){
35294            var g = groups[mi.group];
35295            for(var i = 0, l = g.length; i < l; i++){
35296                if(g[i] != mi){
35297                    g[i].setChecked(false);
35298                }
35299            }
35300        }
35301    }
35302
35303    return {
35304
35305        /**
35306         * Hides all menus that are currently visible
35307         */
35308        hideAll : function(){
35309             hideAll();  
35310        },
35311
35312        // private
35313        register : function(menu){
35314            if(!menus){
35315                init();
35316            }
35317            menus[menu.id] = menu;
35318            menu.on("beforehide", onBeforeHide);
35319            menu.on("hide", onHide);
35320            menu.on("beforeshow", onBeforeShow);
35321            menu.on("show", onShow);
35322            var g = menu.group;
35323            if(g && menu.events["checkchange"]){
35324                if(!groups[g]){
35325                    groups[g] = [];
35326                }
35327                groups[g].push(menu);
35328                menu.on("checkchange", onCheck);
35329            }
35330        },
35331
35332         /**
35333          * Returns a {@link Roo.menu.Menu} object
35334          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
35335          * be used to generate and return a new Menu instance.
35336          */
35337        get : function(menu){
35338            if(typeof menu == "string"){ // menu id
35339                return menus[menu];
35340            }else if(menu.events){  // menu instance
35341                return menu;
35342            }else if(typeof menu.length == 'number'){ // array of menu items?
35343                return new Roo.menu.Menu({items:menu});
35344            }else{ // otherwise, must be a config
35345                return new Roo.menu.Menu(menu);
35346            }
35347        },
35348
35349        // private
35350        unregister : function(menu){
35351            delete menus[menu.id];
35352            menu.un("beforehide", onBeforeHide);
35353            menu.un("hide", onHide);
35354            menu.un("beforeshow", onBeforeShow);
35355            menu.un("show", onShow);
35356            var g = menu.group;
35357            if(g && menu.events["checkchange"]){
35358                groups[g].remove(menu);
35359                menu.un("checkchange", onCheck);
35360            }
35361        },
35362
35363        // private
35364        registerCheckable : function(menuItem){
35365            var g = menuItem.group;
35366            if(g){
35367                if(!groups[g]){
35368                    groups[g] = [];
35369                }
35370                groups[g].push(menuItem);
35371                menuItem.on("beforecheckchange", onBeforeCheck);
35372            }
35373        },
35374
35375        // private
35376        unregisterCheckable : function(menuItem){
35377            var g = menuItem.group;
35378            if(g){
35379                groups[g].remove(menuItem);
35380                menuItem.un("beforecheckchange", onBeforeCheck);
35381            }
35382        }
35383    };
35384 }();/*
35385  * Based on:
35386  * Ext JS Library 1.1.1
35387  * Copyright(c) 2006-2007, Ext JS, LLC.
35388  *
35389  * Originally Released Under LGPL - original licence link has changed is not relivant.
35390  *
35391  * Fork - LGPL
35392  * <script type="text/javascript">
35393  */
35394  
35395
35396 /**
35397  * @class Roo.menu.BaseItem
35398  * @extends Roo.Component
35399  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
35400  * management and base configuration options shared by all menu components.
35401  * @constructor
35402  * Creates a new BaseItem
35403  * @param {Object} config Configuration options
35404  */
35405 Roo.menu.BaseItem = function(config){
35406     Roo.menu.BaseItem.superclass.constructor.call(this, config);
35407
35408     this.addEvents({
35409         /**
35410          * @event click
35411          * Fires when this item is clicked
35412          * @param {Roo.menu.BaseItem} this
35413          * @param {Roo.EventObject} e
35414          */
35415         click: true,
35416         /**
35417          * @event activate
35418          * Fires when this item is activated
35419          * @param {Roo.menu.BaseItem} this
35420          */
35421         activate : true,
35422         /**
35423          * @event deactivate
35424          * Fires when this item is deactivated
35425          * @param {Roo.menu.BaseItem} this
35426          */
35427         deactivate : true
35428     });
35429
35430     if(this.handler){
35431         this.on("click", this.handler, this.scope, true);
35432     }
35433 };
35434
35435 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
35436     /**
35437      * @cfg {Function} handler
35438      * A function that will handle the click event of this menu item (defaults to undefined)
35439      */
35440     /**
35441      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
35442      */
35443     canActivate : false,
35444     
35445      /**
35446      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
35447      */
35448     hidden: false,
35449     
35450     /**
35451      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
35452      */
35453     activeClass : "x-menu-item-active",
35454     /**
35455      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
35456      */
35457     hideOnClick : true,
35458     /**
35459      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
35460      */
35461     hideDelay : 100,
35462
35463     // private
35464     ctype: "Roo.menu.BaseItem",
35465
35466     // private
35467     actionMode : "container",
35468
35469     // private
35470     render : function(container, parentMenu){
35471         this.parentMenu = parentMenu;
35472         Roo.menu.BaseItem.superclass.render.call(this, container);
35473         this.container.menuItemId = this.id;
35474     },
35475
35476     // private
35477     onRender : function(container, position){
35478         this.el = Roo.get(this.el);
35479         container.dom.appendChild(this.el.dom);
35480     },
35481
35482     // private
35483     onClick : function(e){
35484         if(!this.disabled && this.fireEvent("click", this, e) !== false
35485                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
35486             this.handleClick(e);
35487         }else{
35488             e.stopEvent();
35489         }
35490     },
35491
35492     // private
35493     activate : function(){
35494         if(this.disabled){
35495             return false;
35496         }
35497         var li = this.container;
35498         li.addClass(this.activeClass);
35499         this.region = li.getRegion().adjust(2, 2, -2, -2);
35500         this.fireEvent("activate", this);
35501         return true;
35502     },
35503
35504     // private
35505     deactivate : function(){
35506         this.container.removeClass(this.activeClass);
35507         this.fireEvent("deactivate", this);
35508     },
35509
35510     // private
35511     shouldDeactivate : function(e){
35512         return !this.region || !this.region.contains(e.getPoint());
35513     },
35514
35515     // private
35516     handleClick : function(e){
35517         if(this.hideOnClick){
35518             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
35519         }
35520     },
35521
35522     // private
35523     expandMenu : function(autoActivate){
35524         // do nothing
35525     },
35526
35527     // private
35528     hideMenu : function(){
35529         // do nothing
35530     }
35531 });/*
35532  * Based on:
35533  * Ext JS Library 1.1.1
35534  * Copyright(c) 2006-2007, Ext JS, LLC.
35535  *
35536  * Originally Released Under LGPL - original licence link has changed is not relivant.
35537  *
35538  * Fork - LGPL
35539  * <script type="text/javascript">
35540  */
35541  
35542 /**
35543  * @class Roo.menu.Adapter
35544  * @extends Roo.menu.BaseItem
35545  * 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.
35546  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
35547  * @constructor
35548  * Creates a new Adapter
35549  * @param {Object} config Configuration options
35550  */
35551 Roo.menu.Adapter = function(component, config){
35552     Roo.menu.Adapter.superclass.constructor.call(this, config);
35553     this.component = component;
35554 };
35555 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
35556     // private
35557     canActivate : true,
35558
35559     // private
35560     onRender : function(container, position){
35561         this.component.render(container);
35562         this.el = this.component.getEl();
35563     },
35564
35565     // private
35566     activate : function(){
35567         if(this.disabled){
35568             return false;
35569         }
35570         this.component.focus();
35571         this.fireEvent("activate", this);
35572         return true;
35573     },
35574
35575     // private
35576     deactivate : function(){
35577         this.fireEvent("deactivate", this);
35578     },
35579
35580     // private
35581     disable : function(){
35582         this.component.disable();
35583         Roo.menu.Adapter.superclass.disable.call(this);
35584     },
35585
35586     // private
35587     enable : function(){
35588         this.component.enable();
35589         Roo.menu.Adapter.superclass.enable.call(this);
35590     }
35591 });/*
35592  * Based on:
35593  * Ext JS Library 1.1.1
35594  * Copyright(c) 2006-2007, Ext JS, LLC.
35595  *
35596  * Originally Released Under LGPL - original licence link has changed is not relivant.
35597  *
35598  * Fork - LGPL
35599  * <script type="text/javascript">
35600  */
35601
35602 /**
35603  * @class Roo.menu.TextItem
35604  * @extends Roo.menu.BaseItem
35605  * Adds a static text string to a menu, usually used as either a heading or group separator.
35606  * Note: old style constructor with text is still supported.
35607  * 
35608  * @constructor
35609  * Creates a new TextItem
35610  * @param {Object} cfg Configuration
35611  */
35612 Roo.menu.TextItem = function(cfg){
35613     if (typeof(cfg) == 'string') {
35614         this.text = cfg;
35615     } else {
35616         Roo.apply(this,cfg);
35617     }
35618     
35619     Roo.menu.TextItem.superclass.constructor.call(this);
35620 };
35621
35622 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35623     /**
35624      * @cfg {Boolean} text Text to show on item.
35625      */
35626     text : '',
35627     
35628     /**
35629      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35630      */
35631     hideOnClick : false,
35632     /**
35633      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35634      */
35635     itemCls : "x-menu-text",
35636
35637     // private
35638     onRender : function(){
35639         var s = document.createElement("span");
35640         s.className = this.itemCls;
35641         s.innerHTML = this.text;
35642         this.el = s;
35643         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35644     }
35645 });/*
35646  * Based on:
35647  * Ext JS Library 1.1.1
35648  * Copyright(c) 2006-2007, Ext JS, LLC.
35649  *
35650  * Originally Released Under LGPL - original licence link has changed is not relivant.
35651  *
35652  * Fork - LGPL
35653  * <script type="text/javascript">
35654  */
35655
35656 /**
35657  * @class Roo.menu.Separator
35658  * @extends Roo.menu.BaseItem
35659  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35660  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35661  * @constructor
35662  * @param {Object} config Configuration options
35663  */
35664 Roo.menu.Separator = function(config){
35665     Roo.menu.Separator.superclass.constructor.call(this, config);
35666 };
35667
35668 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35669     /**
35670      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35671      */
35672     itemCls : "x-menu-sep",
35673     /**
35674      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35675      */
35676     hideOnClick : false,
35677
35678     // private
35679     onRender : function(li){
35680         var s = document.createElement("span");
35681         s.className = this.itemCls;
35682         s.innerHTML = "&#160;";
35683         this.el = s;
35684         li.addClass("x-menu-sep-li");
35685         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35686     }
35687 });/*
35688  * Based on:
35689  * Ext JS Library 1.1.1
35690  * Copyright(c) 2006-2007, Ext JS, LLC.
35691  *
35692  * Originally Released Under LGPL - original licence link has changed is not relivant.
35693  *
35694  * Fork - LGPL
35695  * <script type="text/javascript">
35696  */
35697 /**
35698  * @class Roo.menu.Item
35699  * @extends Roo.menu.BaseItem
35700  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35701  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35702  * activation and click handling.
35703  * @constructor
35704  * Creates a new Item
35705  * @param {Object} config Configuration options
35706  */
35707 Roo.menu.Item = function(config){
35708     Roo.menu.Item.superclass.constructor.call(this, config);
35709     if(this.menu){
35710         this.menu = Roo.menu.MenuMgr.get(this.menu);
35711     }
35712 };
35713 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35714     
35715     /**
35716      * @cfg {String} text
35717      * The text to show on the menu item.
35718      */
35719     text: '',
35720      /**
35721      * @cfg {String} HTML to render in menu
35722      * The text to show on the menu item (HTML version).
35723      */
35724     html: '',
35725     /**
35726      * @cfg {String} icon
35727      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35728      */
35729     icon: undefined,
35730     /**
35731      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35732      */
35733     itemCls : "x-menu-item",
35734     /**
35735      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35736      */
35737     canActivate : true,
35738     /**
35739      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35740      */
35741     showDelay: 200,
35742     // doc'd in BaseItem
35743     hideDelay: 200,
35744
35745     // private
35746     ctype: "Roo.menu.Item",
35747     
35748     // private
35749     onRender : function(container, position){
35750         var el = document.createElement("a");
35751         el.hideFocus = true;
35752         el.unselectable = "on";
35753         el.href = this.href || "#";
35754         if(this.hrefTarget){
35755             el.target = this.hrefTarget;
35756         }
35757         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35758         
35759         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35760         
35761         el.innerHTML = String.format(
35762                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35763                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35764         this.el = el;
35765         Roo.menu.Item.superclass.onRender.call(this, container, position);
35766     },
35767
35768     /**
35769      * Sets the text to display in this menu item
35770      * @param {String} text The text to display
35771      * @param {Boolean} isHTML true to indicate text is pure html.
35772      */
35773     setText : function(text, isHTML){
35774         if (isHTML) {
35775             this.html = text;
35776         } else {
35777             this.text = text;
35778             this.html = '';
35779         }
35780         if(this.rendered){
35781             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35782      
35783             this.el.update(String.format(
35784                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35785                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35786             this.parentMenu.autoWidth();
35787         }
35788     },
35789
35790     // private
35791     handleClick : function(e){
35792         if(!this.href){ // if no link defined, stop the event automatically
35793             e.stopEvent();
35794         }
35795         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35796     },
35797
35798     // private
35799     activate : function(autoExpand){
35800         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35801             this.focus();
35802             if(autoExpand){
35803                 this.expandMenu();
35804             }
35805         }
35806         return true;
35807     },
35808
35809     // private
35810     shouldDeactivate : function(e){
35811         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35812             if(this.menu && this.menu.isVisible()){
35813                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35814             }
35815             return true;
35816         }
35817         return false;
35818     },
35819
35820     // private
35821     deactivate : function(){
35822         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35823         this.hideMenu();
35824     },
35825
35826     // private
35827     expandMenu : function(autoActivate){
35828         if(!this.disabled && this.menu){
35829             clearTimeout(this.hideTimer);
35830             delete this.hideTimer;
35831             if(!this.menu.isVisible() && !this.showTimer){
35832                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35833             }else if (this.menu.isVisible() && autoActivate){
35834                 this.menu.tryActivate(0, 1);
35835             }
35836         }
35837     },
35838
35839     // private
35840     deferExpand : function(autoActivate){
35841         delete this.showTimer;
35842         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35843         if(autoActivate){
35844             this.menu.tryActivate(0, 1);
35845         }
35846     },
35847
35848     // private
35849     hideMenu : function(){
35850         clearTimeout(this.showTimer);
35851         delete this.showTimer;
35852         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35853             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35854         }
35855     },
35856
35857     // private
35858     deferHide : function(){
35859         delete this.hideTimer;
35860         this.menu.hide();
35861     }
35862 });/*
35863  * Based on:
35864  * Ext JS Library 1.1.1
35865  * Copyright(c) 2006-2007, Ext JS, LLC.
35866  *
35867  * Originally Released Under LGPL - original licence link has changed is not relivant.
35868  *
35869  * Fork - LGPL
35870  * <script type="text/javascript">
35871  */
35872  
35873 /**
35874  * @class Roo.menu.CheckItem
35875  * @extends Roo.menu.Item
35876  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35877  * @constructor
35878  * Creates a new CheckItem
35879  * @param {Object} config Configuration options
35880  */
35881 Roo.menu.CheckItem = function(config){
35882     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35883     this.addEvents({
35884         /**
35885          * @event beforecheckchange
35886          * Fires before the checked value is set, providing an opportunity to cancel if needed
35887          * @param {Roo.menu.CheckItem} this
35888          * @param {Boolean} checked The new checked value that will be set
35889          */
35890         "beforecheckchange" : true,
35891         /**
35892          * @event checkchange
35893          * Fires after the checked value has been set
35894          * @param {Roo.menu.CheckItem} this
35895          * @param {Boolean} checked The checked value that was set
35896          */
35897         "checkchange" : true
35898     });
35899     if(this.checkHandler){
35900         this.on('checkchange', this.checkHandler, this.scope);
35901     }
35902 };
35903 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35904     /**
35905      * @cfg {String} group
35906      * All check items with the same group name will automatically be grouped into a single-select
35907      * radio button group (defaults to '')
35908      */
35909     /**
35910      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35911      */
35912     itemCls : "x-menu-item x-menu-check-item",
35913     /**
35914      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35915      */
35916     groupClass : "x-menu-group-item",
35917
35918     /**
35919      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35920      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35921      * initialized with checked = true will be rendered as checked.
35922      */
35923     checked: false,
35924
35925     // private
35926     ctype: "Roo.menu.CheckItem",
35927
35928     // private
35929     onRender : function(c){
35930         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35931         if(this.group){
35932             this.el.addClass(this.groupClass);
35933         }
35934         Roo.menu.MenuMgr.registerCheckable(this);
35935         if(this.checked){
35936             this.checked = false;
35937             this.setChecked(true, true);
35938         }
35939     },
35940
35941     // private
35942     destroy : function(){
35943         if(this.rendered){
35944             Roo.menu.MenuMgr.unregisterCheckable(this);
35945         }
35946         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35947     },
35948
35949     /**
35950      * Set the checked state of this item
35951      * @param {Boolean} checked The new checked value
35952      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35953      */
35954     setChecked : function(state, suppressEvent){
35955         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35956             if(this.container){
35957                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35958             }
35959             this.checked = state;
35960             if(suppressEvent !== true){
35961                 this.fireEvent("checkchange", this, state);
35962             }
35963         }
35964     },
35965
35966     // private
35967     handleClick : function(e){
35968        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35969            this.setChecked(!this.checked);
35970        }
35971        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35972     }
35973 });/*
35974  * Based on:
35975  * Ext JS Library 1.1.1
35976  * Copyright(c) 2006-2007, Ext JS, LLC.
35977  *
35978  * Originally Released Under LGPL - original licence link has changed is not relivant.
35979  *
35980  * Fork - LGPL
35981  * <script type="text/javascript">
35982  */
35983  
35984 /**
35985  * @class Roo.menu.DateItem
35986  * @extends Roo.menu.Adapter
35987  * A menu item that wraps the {@link Roo.DatPicker} component.
35988  * @constructor
35989  * Creates a new DateItem
35990  * @param {Object} config Configuration options
35991  */
35992 Roo.menu.DateItem = function(config){
35993     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35994     /** The Roo.DatePicker object @type Roo.DatePicker */
35995     this.picker = this.component;
35996     this.addEvents({select: true});
35997     
35998     this.picker.on("render", function(picker){
35999         picker.getEl().swallowEvent("click");
36000         picker.container.addClass("x-menu-date-item");
36001     });
36002
36003     this.picker.on("select", this.onSelect, this);
36004 };
36005
36006 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36007     // private
36008     onSelect : function(picker, date){
36009         this.fireEvent("select", this, date, picker);
36010         Roo.menu.DateItem.superclass.handleClick.call(this);
36011     }
36012 });/*
36013  * Based on:
36014  * Ext JS Library 1.1.1
36015  * Copyright(c) 2006-2007, Ext JS, LLC.
36016  *
36017  * Originally Released Under LGPL - original licence link has changed is not relivant.
36018  *
36019  * Fork - LGPL
36020  * <script type="text/javascript">
36021  */
36022  
36023 /**
36024  * @class Roo.menu.ColorItem
36025  * @extends Roo.menu.Adapter
36026  * A menu item that wraps the {@link Roo.ColorPalette} component.
36027  * @constructor
36028  * Creates a new ColorItem
36029  * @param {Object} config Configuration options
36030  */
36031 Roo.menu.ColorItem = function(config){
36032     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36033     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36034     this.palette = this.component;
36035     this.relayEvents(this.palette, ["select"]);
36036     if(this.selectHandler){
36037         this.on('select', this.selectHandler, this.scope);
36038     }
36039 };
36040 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36041  * Based on:
36042  * Ext JS Library 1.1.1
36043  * Copyright(c) 2006-2007, Ext JS, LLC.
36044  *
36045  * Originally Released Under LGPL - original licence link has changed is not relivant.
36046  *
36047  * Fork - LGPL
36048  * <script type="text/javascript">
36049  */
36050  
36051
36052 /**
36053  * @class Roo.menu.DateMenu
36054  * @extends Roo.menu.Menu
36055  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36056  * @constructor
36057  * Creates a new DateMenu
36058  * @param {Object} config Configuration options
36059  */
36060 Roo.menu.DateMenu = function(config){
36061     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36062     this.plain = true;
36063     var di = new Roo.menu.DateItem(config);
36064     this.add(di);
36065     /**
36066      * The {@link Roo.DatePicker} instance for this DateMenu
36067      * @type DatePicker
36068      */
36069     this.picker = di.picker;
36070     /**
36071      * @event select
36072      * @param {DatePicker} picker
36073      * @param {Date} date
36074      */
36075     this.relayEvents(di, ["select"]);
36076     this.on('beforeshow', function(){
36077         if(this.picker){
36078             this.picker.hideMonthPicker(false);
36079         }
36080     }, this);
36081 };
36082 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36083     cls:'x-date-menu'
36084 });/*
36085  * Based on:
36086  * Ext JS Library 1.1.1
36087  * Copyright(c) 2006-2007, Ext JS, LLC.
36088  *
36089  * Originally Released Under LGPL - original licence link has changed is not relivant.
36090  *
36091  * Fork - LGPL
36092  * <script type="text/javascript">
36093  */
36094  
36095
36096 /**
36097  * @class Roo.menu.ColorMenu
36098  * @extends Roo.menu.Menu
36099  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36100  * @constructor
36101  * Creates a new ColorMenu
36102  * @param {Object} config Configuration options
36103  */
36104 Roo.menu.ColorMenu = function(config){
36105     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36106     this.plain = true;
36107     var ci = new Roo.menu.ColorItem(config);
36108     this.add(ci);
36109     /**
36110      * The {@link Roo.ColorPalette} instance for this ColorMenu
36111      * @type ColorPalette
36112      */
36113     this.palette = ci.palette;
36114     /**
36115      * @event select
36116      * @param {ColorPalette} palette
36117      * @param {String} color
36118      */
36119     this.relayEvents(ci, ["select"]);
36120 };
36121 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36122  * Based on:
36123  * Ext JS Library 1.1.1
36124  * Copyright(c) 2006-2007, Ext JS, LLC.
36125  *
36126  * Originally Released Under LGPL - original licence link has changed is not relivant.
36127  *
36128  * Fork - LGPL
36129  * <script type="text/javascript">
36130  */
36131  
36132 /**
36133  * @class Roo.form.Field
36134  * @extends Roo.BoxComponent
36135  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36136  * @constructor
36137  * Creates a new Field
36138  * @param {Object} config Configuration options
36139  */
36140 Roo.form.Field = function(config){
36141     Roo.form.Field.superclass.constructor.call(this, config);
36142 };
36143
36144 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36145     /**
36146      * @cfg {String} fieldLabel Label to use when rendering a form.
36147      */
36148        /**
36149      * @cfg {String} qtip Mouse over tip
36150      */
36151      
36152     /**
36153      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36154      */
36155     invalidClass : "x-form-invalid",
36156     /**
36157      * @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")
36158      */
36159     invalidText : "The value in this field is invalid",
36160     /**
36161      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36162      */
36163     focusClass : "x-form-focus",
36164     /**
36165      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36166       automatic validation (defaults to "keyup").
36167      */
36168     validationEvent : "keyup",
36169     /**
36170      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36171      */
36172     validateOnBlur : true,
36173     /**
36174      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36175      */
36176     validationDelay : 250,
36177     /**
36178      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36179      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36180      */
36181     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36182     /**
36183      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36184      */
36185     fieldClass : "x-form-field",
36186     /**
36187      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36188      *<pre>
36189 Value         Description
36190 -----------   ----------------------------------------------------------------------
36191 qtip          Display a quick tip when the user hovers over the field
36192 title         Display a default browser title attribute popup
36193 under         Add a block div beneath the field containing the error text
36194 side          Add an error icon to the right of the field with a popup on hover
36195 [element id]  Add the error text directly to the innerHTML of the specified element
36196 </pre>
36197      */
36198     msgTarget : 'qtip',
36199     /**
36200      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36201      */
36202     msgFx : 'normal',
36203
36204     /**
36205      * @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.
36206      */
36207     readOnly : false,
36208
36209     /**
36210      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36211      */
36212     disabled : false,
36213
36214     /**
36215      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36216      */
36217     inputType : undefined,
36218     
36219     /**
36220      * @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).
36221          */
36222         tabIndex : undefined,
36223         
36224     // private
36225     isFormField : true,
36226
36227     // private
36228     hasFocus : false,
36229     /**
36230      * @property {Roo.Element} fieldEl
36231      * Element Containing the rendered Field (with label etc.)
36232      */
36233     /**
36234      * @cfg {Mixed} value A value to initialize this field with.
36235      */
36236     value : undefined,
36237
36238     /**
36239      * @cfg {String} name The field's HTML name attribute.
36240      */
36241     /**
36242      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36243      */
36244
36245         // private ??
36246         initComponent : function(){
36247         Roo.form.Field.superclass.initComponent.call(this);
36248         this.addEvents({
36249             /**
36250              * @event focus
36251              * Fires when this field receives input focus.
36252              * @param {Roo.form.Field} this
36253              */
36254             focus : true,
36255             /**
36256              * @event blur
36257              * Fires when this field loses input focus.
36258              * @param {Roo.form.Field} this
36259              */
36260             blur : true,
36261             /**
36262              * @event specialkey
36263              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36264              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36265              * @param {Roo.form.Field} this
36266              * @param {Roo.EventObject} e The event object
36267              */
36268             specialkey : true,
36269             /**
36270              * @event change
36271              * Fires just before the field blurs if the field value has changed.
36272              * @param {Roo.form.Field} this
36273              * @param {Mixed} newValue The new value
36274              * @param {Mixed} oldValue The original value
36275              */
36276             change : true,
36277             /**
36278              * @event invalid
36279              * Fires after the field has been marked as invalid.
36280              * @param {Roo.form.Field} this
36281              * @param {String} msg The validation message
36282              */
36283             invalid : true,
36284             /**
36285              * @event valid
36286              * Fires after the field has been validated with no errors.
36287              * @param {Roo.form.Field} this
36288              */
36289             valid : true,
36290              /**
36291              * @event keyup
36292              * Fires after the key up
36293              * @param {Roo.form.Field} this
36294              * @param {Roo.EventObject}  e The event Object
36295              */
36296             keyup : true
36297         });
36298     },
36299
36300     /**
36301      * Returns the name attribute of the field if available
36302      * @return {String} name The field name
36303      */
36304     getName: function(){
36305          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36306     },
36307
36308     // private
36309     onRender : function(ct, position){
36310         Roo.form.Field.superclass.onRender.call(this, ct, position);
36311         if(!this.el){
36312             var cfg = this.getAutoCreate();
36313             if(!cfg.name){
36314                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
36315             }
36316             if (!cfg.name.length) {
36317                 delete cfg.name;
36318             }
36319             if(this.inputType){
36320                 cfg.type = this.inputType;
36321             }
36322             this.el = ct.createChild(cfg, position);
36323         }
36324         var type = this.el.dom.type;
36325         if(type){
36326             if(type == 'password'){
36327                 type = 'text';
36328             }
36329             this.el.addClass('x-form-'+type);
36330         }
36331         if(this.readOnly){
36332             this.el.dom.readOnly = true;
36333         }
36334         if(this.tabIndex !== undefined){
36335             this.el.dom.setAttribute('tabIndex', this.tabIndex);
36336         }
36337
36338         this.el.addClass([this.fieldClass, this.cls]);
36339         this.initValue();
36340     },
36341
36342     /**
36343      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
36344      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
36345      * @return {Roo.form.Field} this
36346      */
36347     applyTo : function(target){
36348         this.allowDomMove = false;
36349         this.el = Roo.get(target);
36350         this.render(this.el.dom.parentNode);
36351         return this;
36352     },
36353
36354     // private
36355     initValue : function(){
36356         if(this.value !== undefined){
36357             this.setValue(this.value);
36358         }else if(this.el.dom.value.length > 0){
36359             this.setValue(this.el.dom.value);
36360         }
36361     },
36362
36363     /**
36364      * Returns true if this field has been changed since it was originally loaded and is not disabled.
36365      */
36366     isDirty : function() {
36367         if(this.disabled) {
36368             return false;
36369         }
36370         return String(this.getValue()) !== String(this.originalValue);
36371     },
36372
36373     // private
36374     afterRender : function(){
36375         Roo.form.Field.superclass.afterRender.call(this);
36376         this.initEvents();
36377     },
36378
36379     // private
36380     fireKey : function(e){
36381         //Roo.log('field ' + e.getKey());
36382         if(e.isNavKeyPress()){
36383             this.fireEvent("specialkey", this, e);
36384         }
36385     },
36386
36387     /**
36388      * Resets the current field value to the originally loaded value and clears any validation messages
36389      */
36390     reset : function(){
36391         this.setValue(this.originalValue);
36392         this.clearInvalid();
36393     },
36394
36395     // private
36396     initEvents : function(){
36397         // safari killled keypress - so keydown is now used..
36398         this.el.on("keydown" , this.fireKey,  this);
36399         this.el.on("focus", this.onFocus,  this);
36400         this.el.on("blur", this.onBlur,  this);
36401         this.el.relayEvent('keyup', this);
36402
36403         // reference to original value for reset
36404         this.originalValue = this.getValue();
36405     },
36406
36407     // private
36408     onFocus : function(){
36409         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36410             this.el.addClass(this.focusClass);
36411         }
36412         if(!this.hasFocus){
36413             this.hasFocus = true;
36414             this.startValue = this.getValue();
36415             this.fireEvent("focus", this);
36416         }
36417     },
36418
36419     beforeBlur : Roo.emptyFn,
36420
36421     // private
36422     onBlur : function(){
36423         this.beforeBlur();
36424         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36425             this.el.removeClass(this.focusClass);
36426         }
36427         this.hasFocus = false;
36428         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
36429             this.validate();
36430         }
36431         var v = this.getValue();
36432         if(String(v) !== String(this.startValue)){
36433             this.fireEvent('change', this, v, this.startValue);
36434         }
36435         this.fireEvent("blur", this);
36436     },
36437
36438     /**
36439      * Returns whether or not the field value is currently valid
36440      * @param {Boolean} preventMark True to disable marking the field invalid
36441      * @return {Boolean} True if the value is valid, else false
36442      */
36443     isValid : function(preventMark){
36444         if(this.disabled){
36445             return true;
36446         }
36447         var restore = this.preventMark;
36448         this.preventMark = preventMark === true;
36449         var v = this.validateValue(this.processValue(this.getRawValue()));
36450         this.preventMark = restore;
36451         return v;
36452     },
36453
36454     /**
36455      * Validates the field value
36456      * @return {Boolean} True if the value is valid, else false
36457      */
36458     validate : function(){
36459         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
36460             this.clearInvalid();
36461             return true;
36462         }
36463         return false;
36464     },
36465
36466     processValue : function(value){
36467         return value;
36468     },
36469
36470     // private
36471     // Subclasses should provide the validation implementation by overriding this
36472     validateValue : function(value){
36473         return true;
36474     },
36475
36476     /**
36477      * Mark this field as invalid
36478      * @param {String} msg The validation message
36479      */
36480     markInvalid : function(msg){
36481         if(!this.rendered || this.preventMark){ // not rendered
36482             return;
36483         }
36484         this.el.addClass(this.invalidClass);
36485         msg = msg || this.invalidText;
36486         switch(this.msgTarget){
36487             case 'qtip':
36488                 this.el.dom.qtip = msg;
36489                 this.el.dom.qclass = 'x-form-invalid-tip';
36490                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
36491                     Roo.QuickTips.enable();
36492                 }
36493                 break;
36494             case 'title':
36495                 this.el.dom.title = msg;
36496                 break;
36497             case 'under':
36498                 if(!this.errorEl){
36499                     var elp = this.el.findParent('.x-form-element', 5, true);
36500                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
36501                     this.errorEl.setWidth(elp.getWidth(true)-20);
36502                 }
36503                 this.errorEl.update(msg);
36504                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
36505                 break;
36506             case 'side':
36507                 if(!this.errorIcon){
36508                     var elp = this.el.findParent('.x-form-element', 5, true);
36509                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
36510                 }
36511                 this.alignErrorIcon();
36512                 this.errorIcon.dom.qtip = msg;
36513                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
36514                 this.errorIcon.show();
36515                 this.on('resize', this.alignErrorIcon, this);
36516                 break;
36517             default:
36518                 var t = Roo.getDom(this.msgTarget);
36519                 t.innerHTML = msg;
36520                 t.style.display = this.msgDisplay;
36521                 break;
36522         }
36523         this.fireEvent('invalid', this, msg);
36524     },
36525
36526     // private
36527     alignErrorIcon : function(){
36528         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
36529     },
36530
36531     /**
36532      * Clear any invalid styles/messages for this field
36533      */
36534     clearInvalid : function(){
36535         if(!this.rendered || this.preventMark){ // not rendered
36536             return;
36537         }
36538         this.el.removeClass(this.invalidClass);
36539         switch(this.msgTarget){
36540             case 'qtip':
36541                 this.el.dom.qtip = '';
36542                 break;
36543             case 'title':
36544                 this.el.dom.title = '';
36545                 break;
36546             case 'under':
36547                 if(this.errorEl){
36548                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
36549                 }
36550                 break;
36551             case 'side':
36552                 if(this.errorIcon){
36553                     this.errorIcon.dom.qtip = '';
36554                     this.errorIcon.hide();
36555                     this.un('resize', this.alignErrorIcon, this);
36556                 }
36557                 break;
36558             default:
36559                 var t = Roo.getDom(this.msgTarget);
36560                 t.innerHTML = '';
36561                 t.style.display = 'none';
36562                 break;
36563         }
36564         this.fireEvent('valid', this);
36565     },
36566
36567     /**
36568      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
36569      * @return {Mixed} value The field value
36570      */
36571     getRawValue : function(){
36572         var v = this.el.getValue();
36573         
36574         return v;
36575     },
36576
36577     /**
36578      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
36579      * @return {Mixed} value The field value
36580      */
36581     getValue : function(){
36582         var v = this.el.getValue();
36583          
36584         return v;
36585     },
36586
36587     /**
36588      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
36589      * @param {Mixed} value The value to set
36590      */
36591     setRawValue : function(v){
36592         return this.el.dom.value = (v === null || v === undefined ? '' : v);
36593     },
36594
36595     /**
36596      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
36597      * @param {Mixed} value The value to set
36598      */
36599     setValue : function(v){
36600         this.value = v;
36601         if(this.rendered){
36602             this.el.dom.value = (v === null || v === undefined ? '' : v);
36603              this.validate();
36604         }
36605     },
36606
36607     adjustSize : function(w, h){
36608         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
36609         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
36610         return s;
36611     },
36612
36613     adjustWidth : function(tag, w){
36614         tag = tag.toLowerCase();
36615         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36616             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36617                 if(tag == 'input'){
36618                     return w + 2;
36619                 }
36620                 if(tag = 'textarea'){
36621                     return w-2;
36622                 }
36623             }else if(Roo.isOpera){
36624                 if(tag == 'input'){
36625                     return w + 2;
36626                 }
36627                 if(tag = 'textarea'){
36628                     return w-2;
36629                 }
36630             }
36631         }
36632         return w;
36633     }
36634 });
36635
36636
36637 // anything other than normal should be considered experimental
36638 Roo.form.Field.msgFx = {
36639     normal : {
36640         show: function(msgEl, f){
36641             msgEl.setDisplayed('block');
36642         },
36643
36644         hide : function(msgEl, f){
36645             msgEl.setDisplayed(false).update('');
36646         }
36647     },
36648
36649     slide : {
36650         show: function(msgEl, f){
36651             msgEl.slideIn('t', {stopFx:true});
36652         },
36653
36654         hide : function(msgEl, f){
36655             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36656         }
36657     },
36658
36659     slideRight : {
36660         show: function(msgEl, f){
36661             msgEl.fixDisplay();
36662             msgEl.alignTo(f.el, 'tl-tr');
36663             msgEl.slideIn('l', {stopFx:true});
36664         },
36665
36666         hide : function(msgEl, f){
36667             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36668         }
36669     }
36670 };/*
36671  * Based on:
36672  * Ext JS Library 1.1.1
36673  * Copyright(c) 2006-2007, Ext JS, LLC.
36674  *
36675  * Originally Released Under LGPL - original licence link has changed is not relivant.
36676  *
36677  * Fork - LGPL
36678  * <script type="text/javascript">
36679  */
36680  
36681
36682 /**
36683  * @class Roo.form.TextField
36684  * @extends Roo.form.Field
36685  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36686  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36687  * @constructor
36688  * Creates a new TextField
36689  * @param {Object} config Configuration options
36690  */
36691 Roo.form.TextField = function(config){
36692     Roo.form.TextField.superclass.constructor.call(this, config);
36693     this.addEvents({
36694         /**
36695          * @event autosize
36696          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36697          * according to the default logic, but this event provides a hook for the developer to apply additional
36698          * logic at runtime to resize the field if needed.
36699              * @param {Roo.form.Field} this This text field
36700              * @param {Number} width The new field width
36701              */
36702         autosize : true
36703     });
36704 };
36705
36706 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36707     /**
36708      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36709      */
36710     grow : false,
36711     /**
36712      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36713      */
36714     growMin : 30,
36715     /**
36716      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36717      */
36718     growMax : 800,
36719     /**
36720      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36721      */
36722     vtype : null,
36723     /**
36724      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36725      */
36726     maskRe : null,
36727     /**
36728      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36729      */
36730     disableKeyFilter : false,
36731     /**
36732      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36733      */
36734     allowBlank : true,
36735     /**
36736      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36737      */
36738     minLength : 0,
36739     /**
36740      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36741      */
36742     maxLength : Number.MAX_VALUE,
36743     /**
36744      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36745      */
36746     minLengthText : "The minimum length for this field is {0}",
36747     /**
36748      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36749      */
36750     maxLengthText : "The maximum length for this field is {0}",
36751     /**
36752      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36753      */
36754     selectOnFocus : false,
36755     /**
36756      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36757      */
36758     blankText : "This field is required",
36759     /**
36760      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36761      * If available, this function will be called only after the basic validators all return true, and will be passed the
36762      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36763      */
36764     validator : null,
36765     /**
36766      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36767      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36768      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36769      */
36770     regex : null,
36771     /**
36772      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36773      */
36774     regexText : "",
36775     /**
36776      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
36777      */
36778     emptyText : null,
36779    
36780
36781     // private
36782     initEvents : function()
36783     {
36784         if (this.emptyText) {
36785             this.el.attr('placeholder', this.emptyText);
36786         }
36787         
36788         Roo.form.TextField.superclass.initEvents.call(this);
36789         if(this.validationEvent == 'keyup'){
36790             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36791             this.el.on('keyup', this.filterValidation, this);
36792         }
36793         else if(this.validationEvent !== false){
36794             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36795         }
36796         
36797         if(this.selectOnFocus){
36798             this.on("focus", this.preFocus, this);
36799             
36800         }
36801         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36802             this.el.on("keypress", this.filterKeys, this);
36803         }
36804         if(this.grow){
36805             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36806             this.el.on("click", this.autoSize,  this);
36807         }
36808         if(this.el.is('input[type=password]') && Roo.isSafari){
36809             this.el.on('keydown', this.SafariOnKeyDown, this);
36810         }
36811     },
36812
36813     processValue : function(value){
36814         if(this.stripCharsRe){
36815             var newValue = value.replace(this.stripCharsRe, '');
36816             if(newValue !== value){
36817                 this.setRawValue(newValue);
36818                 return newValue;
36819             }
36820         }
36821         return value;
36822     },
36823
36824     filterValidation : function(e){
36825         if(!e.isNavKeyPress()){
36826             this.validationTask.delay(this.validationDelay);
36827         }
36828     },
36829
36830     // private
36831     onKeyUp : function(e){
36832         if(!e.isNavKeyPress()){
36833             this.autoSize();
36834         }
36835     },
36836
36837     /**
36838      * Resets the current field value to the originally-loaded value and clears any validation messages.
36839      *  
36840      */
36841     reset : function(){
36842         Roo.form.TextField.superclass.reset.call(this);
36843        
36844     },
36845
36846     
36847     // private
36848     preFocus : function(){
36849         
36850         if(this.selectOnFocus){
36851             this.el.dom.select();
36852         }
36853     },
36854
36855     
36856     // private
36857     filterKeys : function(e){
36858         var k = e.getKey();
36859         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36860             return;
36861         }
36862         var c = e.getCharCode(), cc = String.fromCharCode(c);
36863         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36864             return;
36865         }
36866         if(!this.maskRe.test(cc)){
36867             e.stopEvent();
36868         }
36869     },
36870
36871     setValue : function(v){
36872         
36873         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36874         
36875         this.autoSize();
36876     },
36877
36878     /**
36879      * Validates a value according to the field's validation rules and marks the field as invalid
36880      * if the validation fails
36881      * @param {Mixed} value The value to validate
36882      * @return {Boolean} True if the value is valid, else false
36883      */
36884     validateValue : function(value){
36885         if(value.length < 1)  { // if it's blank
36886              if(this.allowBlank){
36887                 this.clearInvalid();
36888                 return true;
36889              }else{
36890                 this.markInvalid(this.blankText);
36891                 return false;
36892              }
36893         }
36894         if(value.length < this.minLength){
36895             this.markInvalid(String.format(this.minLengthText, this.minLength));
36896             return false;
36897         }
36898         if(value.length > this.maxLength){
36899             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36900             return false;
36901         }
36902         if(this.vtype){
36903             var vt = Roo.form.VTypes;
36904             if(!vt[this.vtype](value, this)){
36905                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36906                 return false;
36907             }
36908         }
36909         if(typeof this.validator == "function"){
36910             var msg = this.validator(value);
36911             if(msg !== true){
36912                 this.markInvalid(msg);
36913                 return false;
36914             }
36915         }
36916         if(this.regex && !this.regex.test(value)){
36917             this.markInvalid(this.regexText);
36918             return false;
36919         }
36920         return true;
36921     },
36922
36923     /**
36924      * Selects text in this field
36925      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36926      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36927      */
36928     selectText : function(start, end){
36929         var v = this.getRawValue();
36930         if(v.length > 0){
36931             start = start === undefined ? 0 : start;
36932             end = end === undefined ? v.length : end;
36933             var d = this.el.dom;
36934             if(d.setSelectionRange){
36935                 d.setSelectionRange(start, end);
36936             }else if(d.createTextRange){
36937                 var range = d.createTextRange();
36938                 range.moveStart("character", start);
36939                 range.moveEnd("character", v.length-end);
36940                 range.select();
36941             }
36942         }
36943     },
36944
36945     /**
36946      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36947      * This only takes effect if grow = true, and fires the autosize event.
36948      */
36949     autoSize : function(){
36950         if(!this.grow || !this.rendered){
36951             return;
36952         }
36953         if(!this.metrics){
36954             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36955         }
36956         var el = this.el;
36957         var v = el.dom.value;
36958         var d = document.createElement('div');
36959         d.appendChild(document.createTextNode(v));
36960         v = d.innerHTML;
36961         d = null;
36962         v += "&#160;";
36963         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36964         this.el.setWidth(w);
36965         this.fireEvent("autosize", this, w);
36966     },
36967     
36968     // private
36969     SafariOnKeyDown : function(event)
36970     {
36971         // this is a workaround for a password hang bug on chrome/ webkit.
36972         
36973         var isSelectAll = false;
36974         
36975         if(this.el.dom.selectionEnd > 0){
36976             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
36977         }
36978         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
36979             event.preventDefault();
36980             this.setValue('');
36981             return;
36982         }
36983         
36984         if(isSelectAll){ // backspace and delete key
36985             
36986             event.preventDefault();
36987             // this is very hacky as keydown always get's upper case.
36988             //
36989             var cc = String.fromCharCode(event.getCharCode());
36990             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
36991             
36992         }
36993         
36994         
36995     }
36996 });/*
36997  * Based on:
36998  * Ext JS Library 1.1.1
36999  * Copyright(c) 2006-2007, Ext JS, LLC.
37000  *
37001  * Originally Released Under LGPL - original licence link has changed is not relivant.
37002  *
37003  * Fork - LGPL
37004  * <script type="text/javascript">
37005  */
37006  
37007 /**
37008  * @class Roo.form.Hidden
37009  * @extends Roo.form.TextField
37010  * Simple Hidden element used on forms 
37011  * 
37012  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37013  * 
37014  * @constructor
37015  * Creates a new Hidden form element.
37016  * @param {Object} config Configuration options
37017  */
37018
37019
37020
37021 // easy hidden field...
37022 Roo.form.Hidden = function(config){
37023     Roo.form.Hidden.superclass.constructor.call(this, config);
37024 };
37025   
37026 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37027     fieldLabel:      '',
37028     inputType:      'hidden',
37029     width:          50,
37030     allowBlank:     true,
37031     labelSeparator: '',
37032     hidden:         true,
37033     itemCls :       'x-form-item-display-none'
37034
37035
37036 });
37037
37038
37039 /*
37040  * Based on:
37041  * Ext JS Library 1.1.1
37042  * Copyright(c) 2006-2007, Ext JS, LLC.
37043  *
37044  * Originally Released Under LGPL - original licence link has changed is not relivant.
37045  *
37046  * Fork - LGPL
37047  * <script type="text/javascript">
37048  */
37049  
37050 /**
37051  * @class Roo.form.TriggerField
37052  * @extends Roo.form.TextField
37053  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37054  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37055  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37056  * for which you can provide a custom implementation.  For example:
37057  * <pre><code>
37058 var trigger = new Roo.form.TriggerField();
37059 trigger.onTriggerClick = myTriggerFn;
37060 trigger.applyTo('my-field');
37061 </code></pre>
37062  *
37063  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37064  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37065  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37066  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37067  * @constructor
37068  * Create a new TriggerField.
37069  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37070  * to the base TextField)
37071  */
37072 Roo.form.TriggerField = function(config){
37073     this.mimicing = false;
37074     Roo.form.TriggerField.superclass.constructor.call(this, config);
37075 };
37076
37077 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37078     /**
37079      * @cfg {String} triggerClass A CSS class to apply to the trigger
37080      */
37081     /**
37082      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37083      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37084      */
37085     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37086     /**
37087      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37088      */
37089     hideTrigger:false,
37090
37091     /** @cfg {Boolean} grow @hide */
37092     /** @cfg {Number} growMin @hide */
37093     /** @cfg {Number} growMax @hide */
37094
37095     /**
37096      * @hide 
37097      * @method
37098      */
37099     autoSize: Roo.emptyFn,
37100     // private
37101     monitorTab : true,
37102     // private
37103     deferHeight : true,
37104
37105     
37106     actionMode : 'wrap',
37107     // private
37108     onResize : function(w, h){
37109         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37110         if(typeof w == 'number'){
37111             var x = w - this.trigger.getWidth();
37112             this.el.setWidth(this.adjustWidth('input', x));
37113             this.trigger.setStyle('left', x+'px');
37114         }
37115     },
37116
37117     // private
37118     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37119
37120     // private
37121     getResizeEl : function(){
37122         return this.wrap;
37123     },
37124
37125     // private
37126     getPositionEl : function(){
37127         return this.wrap;
37128     },
37129
37130     // private
37131     alignErrorIcon : function(){
37132         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37133     },
37134
37135     // private
37136     onRender : function(ct, position){
37137         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37138         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37139         this.trigger = this.wrap.createChild(this.triggerConfig ||
37140                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37141         if(this.hideTrigger){
37142             this.trigger.setDisplayed(false);
37143         }
37144         this.initTrigger();
37145         if(!this.width){
37146             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37147         }
37148     },
37149
37150     // private
37151     initTrigger : function(){
37152         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37153         this.trigger.addClassOnOver('x-form-trigger-over');
37154         this.trigger.addClassOnClick('x-form-trigger-click');
37155     },
37156
37157     // private
37158     onDestroy : function(){
37159         if(this.trigger){
37160             this.trigger.removeAllListeners();
37161             this.trigger.remove();
37162         }
37163         if(this.wrap){
37164             this.wrap.remove();
37165         }
37166         Roo.form.TriggerField.superclass.onDestroy.call(this);
37167     },
37168
37169     // private
37170     onFocus : function(){
37171         Roo.form.TriggerField.superclass.onFocus.call(this);
37172         if(!this.mimicing){
37173             this.wrap.addClass('x-trigger-wrap-focus');
37174             this.mimicing = true;
37175             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37176             if(this.monitorTab){
37177                 this.el.on("keydown", this.checkTab, this);
37178             }
37179         }
37180     },
37181
37182     // private
37183     checkTab : function(e){
37184         if(e.getKey() == e.TAB){
37185             this.triggerBlur();
37186         }
37187     },
37188
37189     // private
37190     onBlur : function(){
37191         // do nothing
37192     },
37193
37194     // private
37195     mimicBlur : function(e, t){
37196         if(!this.wrap.contains(t) && this.validateBlur()){
37197             this.triggerBlur();
37198         }
37199     },
37200
37201     // private
37202     triggerBlur : function(){
37203         this.mimicing = false;
37204         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37205         if(this.monitorTab){
37206             this.el.un("keydown", this.checkTab, this);
37207         }
37208         this.wrap.removeClass('x-trigger-wrap-focus');
37209         Roo.form.TriggerField.superclass.onBlur.call(this);
37210     },
37211
37212     // private
37213     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37214     validateBlur : function(e, t){
37215         return true;
37216     },
37217
37218     // private
37219     onDisable : function(){
37220         Roo.form.TriggerField.superclass.onDisable.call(this);
37221         if(this.wrap){
37222             this.wrap.addClass('x-item-disabled');
37223         }
37224     },
37225
37226     // private
37227     onEnable : function(){
37228         Roo.form.TriggerField.superclass.onEnable.call(this);
37229         if(this.wrap){
37230             this.wrap.removeClass('x-item-disabled');
37231         }
37232     },
37233
37234     // private
37235     onShow : function(){
37236         var ae = this.getActionEl();
37237         
37238         if(ae){
37239             ae.dom.style.display = '';
37240             ae.dom.style.visibility = 'visible';
37241         }
37242     },
37243
37244     // private
37245     
37246     onHide : function(){
37247         var ae = this.getActionEl();
37248         ae.dom.style.display = 'none';
37249     },
37250
37251     /**
37252      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37253      * by an implementing function.
37254      * @method
37255      * @param {EventObject} e
37256      */
37257     onTriggerClick : Roo.emptyFn
37258 });
37259
37260 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37261 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37262 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37263 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37264     initComponent : function(){
37265         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37266
37267         this.triggerConfig = {
37268             tag:'span', cls:'x-form-twin-triggers', cn:[
37269             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37270             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37271         ]};
37272     },
37273
37274     getTrigger : function(index){
37275         return this.triggers[index];
37276     },
37277
37278     initTrigger : function(){
37279         var ts = this.trigger.select('.x-form-trigger', true);
37280         this.wrap.setStyle('overflow', 'hidden');
37281         var triggerField = this;
37282         ts.each(function(t, all, index){
37283             t.hide = function(){
37284                 var w = triggerField.wrap.getWidth();
37285                 this.dom.style.display = 'none';
37286                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37287             };
37288             t.show = function(){
37289                 var w = triggerField.wrap.getWidth();
37290                 this.dom.style.display = '';
37291                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37292             };
37293             var triggerIndex = 'Trigger'+(index+1);
37294
37295             if(this['hide'+triggerIndex]){
37296                 t.dom.style.display = 'none';
37297             }
37298             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37299             t.addClassOnOver('x-form-trigger-over');
37300             t.addClassOnClick('x-form-trigger-click');
37301         }, this);
37302         this.triggers = ts.elements;
37303     },
37304
37305     onTrigger1Click : Roo.emptyFn,
37306     onTrigger2Click : Roo.emptyFn
37307 });/*
37308  * Based on:
37309  * Ext JS Library 1.1.1
37310  * Copyright(c) 2006-2007, Ext JS, LLC.
37311  *
37312  * Originally Released Under LGPL - original licence link has changed is not relivant.
37313  *
37314  * Fork - LGPL
37315  * <script type="text/javascript">
37316  */
37317  
37318 /**
37319  * @class Roo.form.TextArea
37320  * @extends Roo.form.TextField
37321  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37322  * support for auto-sizing.
37323  * @constructor
37324  * Creates a new TextArea
37325  * @param {Object} config Configuration options
37326  */
37327 Roo.form.TextArea = function(config){
37328     Roo.form.TextArea.superclass.constructor.call(this, config);
37329     // these are provided exchanges for backwards compat
37330     // minHeight/maxHeight were replaced by growMin/growMax to be
37331     // compatible with TextField growing config values
37332     if(this.minHeight !== undefined){
37333         this.growMin = this.minHeight;
37334     }
37335     if(this.maxHeight !== undefined){
37336         this.growMax = this.maxHeight;
37337     }
37338 };
37339
37340 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
37341     /**
37342      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
37343      */
37344     growMin : 60,
37345     /**
37346      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
37347      */
37348     growMax: 1000,
37349     /**
37350      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
37351      * in the field (equivalent to setting overflow: hidden, defaults to false)
37352      */
37353     preventScrollbars: false,
37354     /**
37355      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37356      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
37357      */
37358
37359     // private
37360     onRender : function(ct, position){
37361         if(!this.el){
37362             this.defaultAutoCreate = {
37363                 tag: "textarea",
37364                 style:"width:300px;height:60px;",
37365                 autocomplete: "off"
37366             };
37367         }
37368         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
37369         if(this.grow){
37370             this.textSizeEl = Roo.DomHelper.append(document.body, {
37371                 tag: "pre", cls: "x-form-grow-sizer"
37372             });
37373             if(this.preventScrollbars){
37374                 this.el.setStyle("overflow", "hidden");
37375             }
37376             this.el.setHeight(this.growMin);
37377         }
37378     },
37379
37380     onDestroy : function(){
37381         if(this.textSizeEl){
37382             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
37383         }
37384         Roo.form.TextArea.superclass.onDestroy.call(this);
37385     },
37386
37387     // private
37388     onKeyUp : function(e){
37389         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
37390             this.autoSize();
37391         }
37392     },
37393
37394     /**
37395      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
37396      * This only takes effect if grow = true, and fires the autosize event if the height changes.
37397      */
37398     autoSize : function(){
37399         if(!this.grow || !this.textSizeEl){
37400             return;
37401         }
37402         var el = this.el;
37403         var v = el.dom.value;
37404         var ts = this.textSizeEl;
37405
37406         ts.innerHTML = '';
37407         ts.appendChild(document.createTextNode(v));
37408         v = ts.innerHTML;
37409
37410         Roo.fly(ts).setWidth(this.el.getWidth());
37411         if(v.length < 1){
37412             v = "&#160;&#160;";
37413         }else{
37414             if(Roo.isIE){
37415                 v = v.replace(/\n/g, '<p>&#160;</p>');
37416             }
37417             v += "&#160;\n&#160;";
37418         }
37419         ts.innerHTML = v;
37420         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
37421         if(h != this.lastHeight){
37422             this.lastHeight = h;
37423             this.el.setHeight(h);
37424             this.fireEvent("autosize", this, h);
37425         }
37426     }
37427 });/*
37428  * Based on:
37429  * Ext JS Library 1.1.1
37430  * Copyright(c) 2006-2007, Ext JS, LLC.
37431  *
37432  * Originally Released Under LGPL - original licence link has changed is not relivant.
37433  *
37434  * Fork - LGPL
37435  * <script type="text/javascript">
37436  */
37437  
37438
37439 /**
37440  * @class Roo.form.NumberField
37441  * @extends Roo.form.TextField
37442  * Numeric text field that provides automatic keystroke filtering and numeric validation.
37443  * @constructor
37444  * Creates a new NumberField
37445  * @param {Object} config Configuration options
37446  */
37447 Roo.form.NumberField = function(config){
37448     Roo.form.NumberField.superclass.constructor.call(this, config);
37449 };
37450
37451 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
37452     /**
37453      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
37454      */
37455     fieldClass: "x-form-field x-form-num-field",
37456     /**
37457      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
37458      */
37459     allowDecimals : true,
37460     /**
37461      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
37462      */
37463     decimalSeparator : ".",
37464     /**
37465      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
37466      */
37467     decimalPrecision : 2,
37468     /**
37469      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
37470      */
37471     allowNegative : true,
37472     /**
37473      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
37474      */
37475     minValue : Number.NEGATIVE_INFINITY,
37476     /**
37477      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
37478      */
37479     maxValue : Number.MAX_VALUE,
37480     /**
37481      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37482      */
37483     minText : "The minimum value for this field is {0}",
37484     /**
37485      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37486      */
37487     maxText : "The maximum value for this field is {0}",
37488     /**
37489      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37490      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37491      */
37492     nanText : "{0} is not a valid number",
37493
37494     // private
37495     initEvents : function(){
37496         Roo.form.NumberField.superclass.initEvents.call(this);
37497         var allowed = "0123456789";
37498         if(this.allowDecimals){
37499             allowed += this.decimalSeparator;
37500         }
37501         if(this.allowNegative){
37502             allowed += "-";
37503         }
37504         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37505         var keyPress = function(e){
37506             var k = e.getKey();
37507             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37508                 return;
37509             }
37510             var c = e.getCharCode();
37511             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37512                 e.stopEvent();
37513             }
37514         };
37515         this.el.on("keypress", keyPress, this);
37516     },
37517
37518     // private
37519     validateValue : function(value){
37520         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
37521             return false;
37522         }
37523         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37524              return true;
37525         }
37526         var num = this.parseValue(value);
37527         if(isNaN(num)){
37528             this.markInvalid(String.format(this.nanText, value));
37529             return false;
37530         }
37531         if(num < this.minValue){
37532             this.markInvalid(String.format(this.minText, this.minValue));
37533             return false;
37534         }
37535         if(num > this.maxValue){
37536             this.markInvalid(String.format(this.maxText, this.maxValue));
37537             return false;
37538         }
37539         return true;
37540     },
37541
37542     getValue : function(){
37543         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
37544     },
37545
37546     // private
37547     parseValue : function(value){
37548         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37549         return isNaN(value) ? '' : value;
37550     },
37551
37552     // private
37553     fixPrecision : function(value){
37554         var nan = isNaN(value);
37555         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37556             return nan ? '' : value;
37557         }
37558         return parseFloat(value).toFixed(this.decimalPrecision);
37559     },
37560
37561     setValue : function(v){
37562         v = this.fixPrecision(v);
37563         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
37564     },
37565
37566     // private
37567     decimalPrecisionFcn : function(v){
37568         return Math.floor(v);
37569     },
37570
37571     beforeBlur : function(){
37572         var v = this.parseValue(this.getRawValue());
37573         if(v){
37574             this.setValue(v);
37575         }
37576     }
37577 });/*
37578  * Based on:
37579  * Ext JS Library 1.1.1
37580  * Copyright(c) 2006-2007, Ext JS, LLC.
37581  *
37582  * Originally Released Under LGPL - original licence link has changed is not relivant.
37583  *
37584  * Fork - LGPL
37585  * <script type="text/javascript">
37586  */
37587  
37588 /**
37589  * @class Roo.form.DateField
37590  * @extends Roo.form.TriggerField
37591  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37592 * @constructor
37593 * Create a new DateField
37594 * @param {Object} config
37595  */
37596 Roo.form.DateField = function(config){
37597     Roo.form.DateField.superclass.constructor.call(this, config);
37598     
37599       this.addEvents({
37600          
37601         /**
37602          * @event select
37603          * Fires when a date is selected
37604              * @param {Roo.form.DateField} combo This combo box
37605              * @param {Date} date The date selected
37606              */
37607         'select' : true
37608          
37609     });
37610     
37611     
37612     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37613     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37614     this.ddMatch = null;
37615     if(this.disabledDates){
37616         var dd = this.disabledDates;
37617         var re = "(?:";
37618         for(var i = 0; i < dd.length; i++){
37619             re += dd[i];
37620             if(i != dd.length-1) re += "|";
37621         }
37622         this.ddMatch = new RegExp(re + ")");
37623     }
37624 };
37625
37626 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
37627     /**
37628      * @cfg {String} format
37629      * The default date format string which can be overriden for localization support.  The format must be
37630      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37631      */
37632     format : "m/d/y",
37633     /**
37634      * @cfg {String} altFormats
37635      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37636      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37637      */
37638     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37639     /**
37640      * @cfg {Array} disabledDays
37641      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37642      */
37643     disabledDays : null,
37644     /**
37645      * @cfg {String} disabledDaysText
37646      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37647      */
37648     disabledDaysText : "Disabled",
37649     /**
37650      * @cfg {Array} disabledDates
37651      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37652      * expression so they are very powerful. Some examples:
37653      * <ul>
37654      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37655      * <li>["03/08", "09/16"] would disable those days for every year</li>
37656      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37657      * <li>["03/../2006"] would disable every day in March 2006</li>
37658      * <li>["^03"] would disable every day in every March</li>
37659      * </ul>
37660      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37661      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37662      */
37663     disabledDates : null,
37664     /**
37665      * @cfg {String} disabledDatesText
37666      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37667      */
37668     disabledDatesText : "Disabled",
37669     /**
37670      * @cfg {Date/String} minValue
37671      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37672      * valid format (defaults to null).
37673      */
37674     minValue : null,
37675     /**
37676      * @cfg {Date/String} maxValue
37677      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37678      * valid format (defaults to null).
37679      */
37680     maxValue : null,
37681     /**
37682      * @cfg {String} minText
37683      * The error text to display when the date in the cell is before minValue (defaults to
37684      * 'The date in this field must be after {minValue}').
37685      */
37686     minText : "The date in this field must be equal to or after {0}",
37687     /**
37688      * @cfg {String} maxText
37689      * The error text to display when the date in the cell is after maxValue (defaults to
37690      * 'The date in this field must be before {maxValue}').
37691      */
37692     maxText : "The date in this field must be equal to or before {0}",
37693     /**
37694      * @cfg {String} invalidText
37695      * The error text to display when the date in the field is invalid (defaults to
37696      * '{value} is not a valid date - it must be in the format {format}').
37697      */
37698     invalidText : "{0} is not a valid date - it must be in the format {1}",
37699     /**
37700      * @cfg {String} triggerClass
37701      * An additional CSS class used to style the trigger button.  The trigger will always get the
37702      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37703      * which displays a calendar icon).
37704      */
37705     triggerClass : 'x-form-date-trigger',
37706     
37707
37708     /**
37709      * @cfg {Boolean} useIso
37710      * if enabled, then the date field will use a hidden field to store the 
37711      * real value as iso formated date. default (false)
37712      */ 
37713     useIso : false,
37714     /**
37715      * @cfg {String/Object} autoCreate
37716      * A DomHelper element spec, or true for a default element spec (defaults to
37717      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37718      */ 
37719     // private
37720     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37721     
37722     // private
37723     hiddenField: false,
37724     
37725     onRender : function(ct, position)
37726     {
37727         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37728         if (this.useIso) {
37729             //this.el.dom.removeAttribute('name'); 
37730             Roo.log("Changing name?");
37731             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
37732             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37733                     'before', true);
37734             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
37735             // prevent input submission
37736             this.hiddenName = this.name;
37737         }
37738             
37739             
37740     },
37741     
37742     // private
37743     validateValue : function(value)
37744     {
37745         value = this.formatDate(value);
37746         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37747             Roo.log('super failed');
37748             return false;
37749         }
37750         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37751              return true;
37752         }
37753         var svalue = value;
37754         value = this.parseDate(value);
37755         if(!value){
37756             Roo.log('parse date failed' + svalue);
37757             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37758             return false;
37759         }
37760         var time = value.getTime();
37761         if(this.minValue && time < this.minValue.getTime()){
37762             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37763             return false;
37764         }
37765         if(this.maxValue && time > this.maxValue.getTime()){
37766             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37767             return false;
37768         }
37769         if(this.disabledDays){
37770             var day = value.getDay();
37771             for(var i = 0; i < this.disabledDays.length; i++) {
37772                 if(day === this.disabledDays[i]){
37773                     this.markInvalid(this.disabledDaysText);
37774                     return false;
37775                 }
37776             }
37777         }
37778         var fvalue = this.formatDate(value);
37779         if(this.ddMatch && this.ddMatch.test(fvalue)){
37780             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37781             return false;
37782         }
37783         return true;
37784     },
37785
37786     // private
37787     // Provides logic to override the default TriggerField.validateBlur which just returns true
37788     validateBlur : function(){
37789         return !this.menu || !this.menu.isVisible();
37790     },
37791     
37792     getName: function()
37793     {
37794         // returns hidden if it's set..
37795         if (!this.rendered) {return ''};
37796         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
37797         
37798     },
37799
37800     /**
37801      * Returns the current date value of the date field.
37802      * @return {Date} The date value
37803      */
37804     getValue : function(){
37805         
37806         return  this.hiddenField ?
37807                 this.hiddenField.value :
37808                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37809     },
37810
37811     /**
37812      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37813      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37814      * (the default format used is "m/d/y").
37815      * <br />Usage:
37816      * <pre><code>
37817 //All of these calls set the same date value (May 4, 2006)
37818
37819 //Pass a date object:
37820 var dt = new Date('5/4/06');
37821 dateField.setValue(dt);
37822
37823 //Pass a date string (default format):
37824 dateField.setValue('5/4/06');
37825
37826 //Pass a date string (custom format):
37827 dateField.format = 'Y-m-d';
37828 dateField.setValue('2006-5-4');
37829 </code></pre>
37830      * @param {String/Date} date The date or valid date string
37831      */
37832     setValue : function(date){
37833         if (this.hiddenField) {
37834             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37835         }
37836         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37837         // make sure the value field is always stored as a date..
37838         this.value = this.parseDate(date);
37839         
37840         
37841     },
37842
37843     // private
37844     parseDate : function(value){
37845         if(!value || value instanceof Date){
37846             return value;
37847         }
37848         var v = Date.parseDate(value, this.format);
37849          if (!v && this.useIso) {
37850             v = Date.parseDate(value, 'Y-m-d');
37851         }
37852         if(!v && this.altFormats){
37853             if(!this.altFormatsArray){
37854                 this.altFormatsArray = this.altFormats.split("|");
37855             }
37856             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37857                 v = Date.parseDate(value, this.altFormatsArray[i]);
37858             }
37859         }
37860         return v;
37861     },
37862
37863     // private
37864     formatDate : function(date, fmt){
37865         return (!date || !(date instanceof Date)) ?
37866                date : date.dateFormat(fmt || this.format);
37867     },
37868
37869     // private
37870     menuListeners : {
37871         select: function(m, d){
37872             
37873             this.setValue(d);
37874             this.fireEvent('select', this, d);
37875         },
37876         show : function(){ // retain focus styling
37877             this.onFocus();
37878         },
37879         hide : function(){
37880             this.focus.defer(10, this);
37881             var ml = this.menuListeners;
37882             this.menu.un("select", ml.select,  this);
37883             this.menu.un("show", ml.show,  this);
37884             this.menu.un("hide", ml.hide,  this);
37885         }
37886     },
37887
37888     // private
37889     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37890     onTriggerClick : function(){
37891         if(this.disabled){
37892             return;
37893         }
37894         if(this.menu == null){
37895             this.menu = new Roo.menu.DateMenu();
37896         }
37897         Roo.apply(this.menu.picker,  {
37898             showClear: this.allowBlank,
37899             minDate : this.minValue,
37900             maxDate : this.maxValue,
37901             disabledDatesRE : this.ddMatch,
37902             disabledDatesText : this.disabledDatesText,
37903             disabledDays : this.disabledDays,
37904             disabledDaysText : this.disabledDaysText,
37905             format : this.useIso ? 'Y-m-d' : this.format,
37906             minText : String.format(this.minText, this.formatDate(this.minValue)),
37907             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37908         });
37909         this.menu.on(Roo.apply({}, this.menuListeners, {
37910             scope:this
37911         }));
37912         this.menu.picker.setValue(this.getValue() || new Date());
37913         this.menu.show(this.el, "tl-bl?");
37914     },
37915
37916     beforeBlur : function(){
37917         var v = this.parseDate(this.getRawValue());
37918         if(v){
37919             this.setValue(v);
37920         }
37921     }
37922
37923     /** @cfg {Boolean} grow @hide */
37924     /** @cfg {Number} growMin @hide */
37925     /** @cfg {Number} growMax @hide */
37926     /**
37927      * @hide
37928      * @method autoSize
37929      */
37930 });/*
37931  * Based on:
37932  * Ext JS Library 1.1.1
37933  * Copyright(c) 2006-2007, Ext JS, LLC.
37934  *
37935  * Originally Released Under LGPL - original licence link has changed is not relivant.
37936  *
37937  * Fork - LGPL
37938  * <script type="text/javascript">
37939  */
37940  
37941 /**
37942  * @class Roo.form.MonthField
37943  * @extends Roo.form.TriggerField
37944  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37945 * @constructor
37946 * Create a new MonthField
37947 * @param {Object} config
37948  */
37949 Roo.form.MonthField = function(config){
37950     
37951     Roo.form.MonthField.superclass.constructor.call(this, config);
37952     
37953       this.addEvents({
37954          
37955         /**
37956          * @event select
37957          * Fires when a date is selected
37958              * @param {Roo.form.MonthFieeld} combo This combo box
37959              * @param {Date} date The date selected
37960              */
37961         'select' : true
37962          
37963     });
37964     
37965     
37966     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37967     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37968     this.ddMatch = null;
37969     if(this.disabledDates){
37970         var dd = this.disabledDates;
37971         var re = "(?:";
37972         for(var i = 0; i < dd.length; i++){
37973             re += dd[i];
37974             if(i != dd.length-1) re += "|";
37975         }
37976         this.ddMatch = new RegExp(re + ")");
37977     }
37978 };
37979
37980 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
37981     /**
37982      * @cfg {String} format
37983      * The default date format string which can be overriden for localization support.  The format must be
37984      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37985      */
37986     format : "M Y",
37987     /**
37988      * @cfg {String} altFormats
37989      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37990      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37991      */
37992     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
37993     /**
37994      * @cfg {Array} disabledDays
37995      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37996      */
37997     disabledDays : [0,1,2,3,4,5,6],
37998     /**
37999      * @cfg {String} disabledDaysText
38000      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38001      */
38002     disabledDaysText : "Disabled",
38003     /**
38004      * @cfg {Array} disabledDates
38005      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38006      * expression so they are very powerful. Some examples:
38007      * <ul>
38008      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38009      * <li>["03/08", "09/16"] would disable those days for every year</li>
38010      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38011      * <li>["03/../2006"] would disable every day in March 2006</li>
38012      * <li>["^03"] would disable every day in every March</li>
38013      * </ul>
38014      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38015      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38016      */
38017     disabledDates : null,
38018     /**
38019      * @cfg {String} disabledDatesText
38020      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38021      */
38022     disabledDatesText : "Disabled",
38023     /**
38024      * @cfg {Date/String} minValue
38025      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38026      * valid format (defaults to null).
38027      */
38028     minValue : null,
38029     /**
38030      * @cfg {Date/String} maxValue
38031      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38032      * valid format (defaults to null).
38033      */
38034     maxValue : null,
38035     /**
38036      * @cfg {String} minText
38037      * The error text to display when the date in the cell is before minValue (defaults to
38038      * 'The date in this field must be after {minValue}').
38039      */
38040     minText : "The date in this field must be equal to or after {0}",
38041     /**
38042      * @cfg {String} maxTextf
38043      * The error text to display when the date in the cell is after maxValue (defaults to
38044      * 'The date in this field must be before {maxValue}').
38045      */
38046     maxText : "The date in this field must be equal to or before {0}",
38047     /**
38048      * @cfg {String} invalidText
38049      * The error text to display when the date in the field is invalid (defaults to
38050      * '{value} is not a valid date - it must be in the format {format}').
38051      */
38052     invalidText : "{0} is not a valid date - it must be in the format {1}",
38053     /**
38054      * @cfg {String} triggerClass
38055      * An additional CSS class used to style the trigger button.  The trigger will always get the
38056      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38057      * which displays a calendar icon).
38058      */
38059     triggerClass : 'x-form-date-trigger',
38060     
38061
38062     /**
38063      * @cfg {Boolean} useIso
38064      * if enabled, then the date field will use a hidden field to store the 
38065      * real value as iso formated date. default (true)
38066      */ 
38067     useIso : true,
38068     /**
38069      * @cfg {String/Object} autoCreate
38070      * A DomHelper element spec, or true for a default element spec (defaults to
38071      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38072      */ 
38073     // private
38074     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38075     
38076     // private
38077     hiddenField: false,
38078     
38079     hideMonthPicker : false,
38080     
38081     onRender : function(ct, position)
38082     {
38083         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38084         if (this.useIso) {
38085             this.el.dom.removeAttribute('name'); 
38086             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38087                     'before', true);
38088             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38089             // prevent input submission
38090             this.hiddenName = this.name;
38091         }
38092             
38093             
38094     },
38095     
38096     // private
38097     validateValue : function(value)
38098     {
38099         value = this.formatDate(value);
38100         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38101             return false;
38102         }
38103         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38104              return true;
38105         }
38106         var svalue = value;
38107         value = this.parseDate(value);
38108         if(!value){
38109             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38110             return false;
38111         }
38112         var time = value.getTime();
38113         if(this.minValue && time < this.minValue.getTime()){
38114             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38115             return false;
38116         }
38117         if(this.maxValue && time > this.maxValue.getTime()){
38118             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38119             return false;
38120         }
38121         /*if(this.disabledDays){
38122             var day = value.getDay();
38123             for(var i = 0; i < this.disabledDays.length; i++) {
38124                 if(day === this.disabledDays[i]){
38125                     this.markInvalid(this.disabledDaysText);
38126                     return false;
38127                 }
38128             }
38129         }
38130         */
38131         var fvalue = this.formatDate(value);
38132         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38133             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38134             return false;
38135         }
38136         */
38137         return true;
38138     },
38139
38140     // private
38141     // Provides logic to override the default TriggerField.validateBlur which just returns true
38142     validateBlur : function(){
38143         return !this.menu || !this.menu.isVisible();
38144     },
38145
38146     /**
38147      * Returns the current date value of the date field.
38148      * @return {Date} The date value
38149      */
38150     getValue : function(){
38151         
38152         
38153         
38154         return  this.hiddenField ?
38155                 this.hiddenField.value :
38156                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38157     },
38158
38159     /**
38160      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38161      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38162      * (the default format used is "m/d/y").
38163      * <br />Usage:
38164      * <pre><code>
38165 //All of these calls set the same date value (May 4, 2006)
38166
38167 //Pass a date object:
38168 var dt = new Date('5/4/06');
38169 monthField.setValue(dt);
38170
38171 //Pass a date string (default format):
38172 monthField.setValue('5/4/06');
38173
38174 //Pass a date string (custom format):
38175 monthField.format = 'Y-m-d';
38176 monthField.setValue('2006-5-4');
38177 </code></pre>
38178      * @param {String/Date} date The date or valid date string
38179      */
38180     setValue : function(date){
38181         Roo.log('month setValue' + date);
38182         // can only be first of month..
38183         
38184         var val = this.parseDate(date);
38185         
38186         if (this.hiddenField) {
38187             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38188         }
38189         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38190         this.value = this.parseDate(date);
38191     },
38192
38193     // private
38194     parseDate : function(value){
38195         if(!value || value instanceof Date){
38196             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38197             return value;
38198         }
38199         var v = Date.parseDate(value, this.format);
38200         if (!v && this.useIso) {
38201             v = Date.parseDate(value, 'Y-m-d');
38202         }
38203         if (v) {
38204             // 
38205             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38206         }
38207         
38208         
38209         if(!v && this.altFormats){
38210             if(!this.altFormatsArray){
38211                 this.altFormatsArray = this.altFormats.split("|");
38212             }
38213             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38214                 v = Date.parseDate(value, this.altFormatsArray[i]);
38215             }
38216         }
38217         return v;
38218     },
38219
38220     // private
38221     formatDate : function(date, fmt){
38222         return (!date || !(date instanceof Date)) ?
38223                date : date.dateFormat(fmt || this.format);
38224     },
38225
38226     // private
38227     menuListeners : {
38228         select: function(m, d){
38229             this.setValue(d);
38230             this.fireEvent('select', this, d);
38231         },
38232         show : function(){ // retain focus styling
38233             this.onFocus();
38234         },
38235         hide : function(){
38236             this.focus.defer(10, this);
38237             var ml = this.menuListeners;
38238             this.menu.un("select", ml.select,  this);
38239             this.menu.un("show", ml.show,  this);
38240             this.menu.un("hide", ml.hide,  this);
38241         }
38242     },
38243     // private
38244     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38245     onTriggerClick : function(){
38246         if(this.disabled){
38247             return;
38248         }
38249         if(this.menu == null){
38250             this.menu = new Roo.menu.DateMenu();
38251            
38252         }
38253         
38254         Roo.apply(this.menu.picker,  {
38255             
38256             showClear: this.allowBlank,
38257             minDate : this.minValue,
38258             maxDate : this.maxValue,
38259             disabledDatesRE : this.ddMatch,
38260             disabledDatesText : this.disabledDatesText,
38261             
38262             format : this.useIso ? 'Y-m-d' : this.format,
38263             minText : String.format(this.minText, this.formatDate(this.minValue)),
38264             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38265             
38266         });
38267          this.menu.on(Roo.apply({}, this.menuListeners, {
38268             scope:this
38269         }));
38270        
38271         
38272         var m = this.menu;
38273         var p = m.picker;
38274         
38275         // hide month picker get's called when we called by 'before hide';
38276         
38277         var ignorehide = true;
38278         p.hideMonthPicker  = function(disableAnim){
38279             if (ignorehide) {
38280                 return;
38281             }
38282              if(this.monthPicker){
38283                 Roo.log("hideMonthPicker called");
38284                 if(disableAnim === true){
38285                     this.monthPicker.hide();
38286                 }else{
38287                     this.monthPicker.slideOut('t', {duration:.2});
38288                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38289                     p.fireEvent("select", this, this.value);
38290                     m.hide();
38291                 }
38292             }
38293         }
38294         
38295         Roo.log('picker set value');
38296         Roo.log(this.getValue());
38297         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38298         m.show(this.el, 'tl-bl?');
38299         ignorehide  = false;
38300         // this will trigger hideMonthPicker..
38301         
38302         
38303         // hidden the day picker
38304         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38305         
38306         
38307         
38308       
38309         
38310         p.showMonthPicker.defer(100, p);
38311     
38312         
38313        
38314     },
38315
38316     beforeBlur : function(){
38317         var v = this.parseDate(this.getRawValue());
38318         if(v){
38319             this.setValue(v);
38320         }
38321     }
38322
38323     /** @cfg {Boolean} grow @hide */
38324     /** @cfg {Number} growMin @hide */
38325     /** @cfg {Number} growMax @hide */
38326     /**
38327      * @hide
38328      * @method autoSize
38329      */
38330 });/*
38331  * Based on:
38332  * Ext JS Library 1.1.1
38333  * Copyright(c) 2006-2007, Ext JS, LLC.
38334  *
38335  * Originally Released Under LGPL - original licence link has changed is not relivant.
38336  *
38337  * Fork - LGPL
38338  * <script type="text/javascript">
38339  */
38340  
38341
38342 /**
38343  * @class Roo.form.ComboBox
38344  * @extends Roo.form.TriggerField
38345  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
38346  * @constructor
38347  * Create a new ComboBox.
38348  * @param {Object} config Configuration options
38349  */
38350 Roo.form.ComboBox = function(config){
38351     Roo.form.ComboBox.superclass.constructor.call(this, config);
38352     this.addEvents({
38353         /**
38354          * @event expand
38355          * Fires when the dropdown list is expanded
38356              * @param {Roo.form.ComboBox} combo This combo box
38357              */
38358         'expand' : true,
38359         /**
38360          * @event collapse
38361          * Fires when the dropdown list is collapsed
38362              * @param {Roo.form.ComboBox} combo This combo box
38363              */
38364         'collapse' : true,
38365         /**
38366          * @event beforeselect
38367          * Fires before a list item is selected. Return false to cancel the selection.
38368              * @param {Roo.form.ComboBox} combo This combo box
38369              * @param {Roo.data.Record} record The data record returned from the underlying store
38370              * @param {Number} index The index of the selected item in the dropdown list
38371              */
38372         'beforeselect' : true,
38373         /**
38374          * @event select
38375          * Fires when a list item is selected
38376              * @param {Roo.form.ComboBox} combo This combo box
38377              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
38378              * @param {Number} index The index of the selected item in the dropdown list
38379              */
38380         'select' : true,
38381         /**
38382          * @event beforequery
38383          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
38384          * The event object passed has these properties:
38385              * @param {Roo.form.ComboBox} combo This combo box
38386              * @param {String} query The query
38387              * @param {Boolean} forceAll true to force "all" query
38388              * @param {Boolean} cancel true to cancel the query
38389              * @param {Object} e The query event object
38390              */
38391         'beforequery': true,
38392          /**
38393          * @event add
38394          * Fires when the 'add' icon is pressed (add a listener to enable add button)
38395              * @param {Roo.form.ComboBox} combo This combo box
38396              */
38397         'add' : true,
38398         /**
38399          * @event edit
38400          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
38401              * @param {Roo.form.ComboBox} combo This combo box
38402              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
38403              */
38404         'edit' : true
38405         
38406         
38407     });
38408     if(this.transform){
38409         this.allowDomMove = false;
38410         var s = Roo.getDom(this.transform);
38411         if(!this.hiddenName){
38412             this.hiddenName = s.name;
38413         }
38414         if(!this.store){
38415             this.mode = 'local';
38416             var d = [], opts = s.options;
38417             for(var i = 0, len = opts.length;i < len; i++){
38418                 var o = opts[i];
38419                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
38420                 if(o.selected) {
38421                     this.value = value;
38422                 }
38423                 d.push([value, o.text]);
38424             }
38425             this.store = new Roo.data.SimpleStore({
38426                 'id': 0,
38427                 fields: ['value', 'text'],
38428                 data : d
38429             });
38430             this.valueField = 'value';
38431             this.displayField = 'text';
38432         }
38433         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
38434         if(!this.lazyRender){
38435             this.target = true;
38436             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
38437             s.parentNode.removeChild(s); // remove it
38438             this.render(this.el.parentNode);
38439         }else{
38440             s.parentNode.removeChild(s); // remove it
38441         }
38442
38443     }
38444     if (this.store) {
38445         this.store = Roo.factory(this.store, Roo.data);
38446     }
38447     
38448     this.selectedIndex = -1;
38449     if(this.mode == 'local'){
38450         if(config.queryDelay === undefined){
38451             this.queryDelay = 10;
38452         }
38453         if(config.minChars === undefined){
38454             this.minChars = 0;
38455         }
38456     }
38457 };
38458
38459 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
38460     /**
38461      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
38462      */
38463     /**
38464      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
38465      * rendering into an Roo.Editor, defaults to false)
38466      */
38467     /**
38468      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
38469      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
38470      */
38471     /**
38472      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
38473      */
38474     /**
38475      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
38476      * the dropdown list (defaults to undefined, with no header element)
38477      */
38478
38479      /**
38480      * @cfg {String/Roo.Template} tpl The template to use to render the output
38481      */
38482      
38483     // private
38484     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
38485     /**
38486      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
38487      */
38488     listWidth: undefined,
38489     /**
38490      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
38491      * mode = 'remote' or 'text' if mode = 'local')
38492      */
38493     displayField: undefined,
38494     /**
38495      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
38496      * mode = 'remote' or 'value' if mode = 'local'). 
38497      * Note: use of a valueField requires the user make a selection
38498      * in order for a value to be mapped.
38499      */
38500     valueField: undefined,
38501     
38502     
38503     /**
38504      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
38505      * field's data value (defaults to the underlying DOM element's name)
38506      */
38507     hiddenName: undefined,
38508     /**
38509      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
38510      */
38511     listClass: '',
38512     /**
38513      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
38514      */
38515     selectedClass: 'x-combo-selected',
38516     /**
38517      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38518      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
38519      * which displays a downward arrow icon).
38520      */
38521     triggerClass : 'x-form-arrow-trigger',
38522     /**
38523      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
38524      */
38525     shadow:'sides',
38526     /**
38527      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
38528      * anchor positions (defaults to 'tl-bl')
38529      */
38530     listAlign: 'tl-bl?',
38531     /**
38532      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
38533      */
38534     maxHeight: 300,
38535     /**
38536      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
38537      * query specified by the allQuery config option (defaults to 'query')
38538      */
38539     triggerAction: 'query',
38540     /**
38541      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
38542      * (defaults to 4, does not apply if editable = false)
38543      */
38544     minChars : 4,
38545     /**
38546      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
38547      * delay (typeAheadDelay) if it matches a known value (defaults to false)
38548      */
38549     typeAhead: false,
38550     /**
38551      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
38552      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
38553      */
38554     queryDelay: 500,
38555     /**
38556      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
38557      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
38558      */
38559     pageSize: 0,
38560     /**
38561      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
38562      * when editable = true (defaults to false)
38563      */
38564     selectOnFocus:false,
38565     /**
38566      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
38567      */
38568     queryParam: 'query',
38569     /**
38570      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
38571      * when mode = 'remote' (defaults to 'Loading...')
38572      */
38573     loadingText: 'Loading...',
38574     /**
38575      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
38576      */
38577     resizable: false,
38578     /**
38579      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
38580      */
38581     handleHeight : 8,
38582     /**
38583      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
38584      * traditional select (defaults to true)
38585      */
38586     editable: true,
38587     /**
38588      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
38589      */
38590     allQuery: '',
38591     /**
38592      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
38593      */
38594     mode: 'remote',
38595     /**
38596      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
38597      * listWidth has a higher value)
38598      */
38599     minListWidth : 70,
38600     /**
38601      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
38602      * allow the user to set arbitrary text into the field (defaults to false)
38603      */
38604     forceSelection:false,
38605     /**
38606      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
38607      * if typeAhead = true (defaults to 250)
38608      */
38609     typeAheadDelay : 250,
38610     /**
38611      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
38612      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
38613      */
38614     valueNotFoundText : undefined,
38615     /**
38616      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
38617      */
38618     blockFocus : false,
38619     
38620     /**
38621      * @cfg {Boolean} disableClear Disable showing of clear button.
38622      */
38623     disableClear : false,
38624     /**
38625      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
38626      */
38627     alwaysQuery : false,
38628     
38629     //private
38630     addicon : false,
38631     editicon: false,
38632     
38633     // element that contains real text value.. (when hidden is used..)
38634      
38635     // private
38636     onRender : function(ct, position){
38637         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
38638         if(this.hiddenName){
38639             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
38640                     'before', true);
38641             this.hiddenField.value =
38642                 this.hiddenValue !== undefined ? this.hiddenValue :
38643                 this.value !== undefined ? this.value : '';
38644
38645             // prevent input submission
38646             this.el.dom.removeAttribute('name');
38647              
38648              
38649         }
38650         if(Roo.isGecko){
38651             this.el.dom.setAttribute('autocomplete', 'off');
38652         }
38653
38654         var cls = 'x-combo-list';
38655
38656         this.list = new Roo.Layer({
38657             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
38658         });
38659
38660         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
38661         this.list.setWidth(lw);
38662         this.list.swallowEvent('mousewheel');
38663         this.assetHeight = 0;
38664
38665         if(this.title){
38666             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
38667             this.assetHeight += this.header.getHeight();
38668         }
38669
38670         this.innerList = this.list.createChild({cls:cls+'-inner'});
38671         this.innerList.on('mouseover', this.onViewOver, this);
38672         this.innerList.on('mousemove', this.onViewMove, this);
38673         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38674         
38675         if(this.allowBlank && !this.pageSize && !this.disableClear){
38676             this.footer = this.list.createChild({cls:cls+'-ft'});
38677             this.pageTb = new Roo.Toolbar(this.footer);
38678            
38679         }
38680         if(this.pageSize){
38681             this.footer = this.list.createChild({cls:cls+'-ft'});
38682             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
38683                     {pageSize: this.pageSize});
38684             
38685         }
38686         
38687         if (this.pageTb && this.allowBlank && !this.disableClear) {
38688             var _this = this;
38689             this.pageTb.add(new Roo.Toolbar.Fill(), {
38690                 cls: 'x-btn-icon x-btn-clear',
38691                 text: '&#160;',
38692                 handler: function()
38693                 {
38694                     _this.collapse();
38695                     _this.clearValue();
38696                     _this.onSelect(false, -1);
38697                 }
38698             });
38699         }
38700         if (this.footer) {
38701             this.assetHeight += this.footer.getHeight();
38702         }
38703         
38704
38705         if(!this.tpl){
38706             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
38707         }
38708
38709         this.view = new Roo.View(this.innerList, this.tpl, {
38710             singleSelect:true, store: this.store, selectedClass: this.selectedClass
38711         });
38712
38713         this.view.on('click', this.onViewClick, this);
38714
38715         this.store.on('beforeload', this.onBeforeLoad, this);
38716         this.store.on('load', this.onLoad, this);
38717         this.store.on('loadexception', this.onLoadException, this);
38718
38719         if(this.resizable){
38720             this.resizer = new Roo.Resizable(this.list,  {
38721                pinned:true, handles:'se'
38722             });
38723             this.resizer.on('resize', function(r, w, h){
38724                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
38725                 this.listWidth = w;
38726                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
38727                 this.restrictHeight();
38728             }, this);
38729             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
38730         }
38731         if(!this.editable){
38732             this.editable = true;
38733             this.setEditable(false);
38734         }  
38735         
38736         
38737         if (typeof(this.events.add.listeners) != 'undefined') {
38738             
38739             this.addicon = this.wrap.createChild(
38740                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
38741        
38742             this.addicon.on('click', function(e) {
38743                 this.fireEvent('add', this);
38744             }, this);
38745         }
38746         if (typeof(this.events.edit.listeners) != 'undefined') {
38747             
38748             this.editicon = this.wrap.createChild(
38749                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
38750             if (this.addicon) {
38751                 this.editicon.setStyle('margin-left', '40px');
38752             }
38753             this.editicon.on('click', function(e) {
38754                 
38755                 // we fire even  if inothing is selected..
38756                 this.fireEvent('edit', this, this.lastData );
38757                 
38758             }, this);
38759         }
38760         
38761         
38762         
38763     },
38764
38765     // private
38766     initEvents : function(){
38767         Roo.form.ComboBox.superclass.initEvents.call(this);
38768
38769         this.keyNav = new Roo.KeyNav(this.el, {
38770             "up" : function(e){
38771                 this.inKeyMode = true;
38772                 this.selectPrev();
38773             },
38774
38775             "down" : function(e){
38776                 if(!this.isExpanded()){
38777                     this.onTriggerClick();
38778                 }else{
38779                     this.inKeyMode = true;
38780                     this.selectNext();
38781                 }
38782             },
38783
38784             "enter" : function(e){
38785                 this.onViewClick();
38786                 //return true;
38787             },
38788
38789             "esc" : function(e){
38790                 this.collapse();
38791             },
38792
38793             "tab" : function(e){
38794                 this.onViewClick(false);
38795                 this.fireEvent("specialkey", this, e);
38796                 return true;
38797             },
38798
38799             scope : this,
38800
38801             doRelay : function(foo, bar, hname){
38802                 if(hname == 'down' || this.scope.isExpanded()){
38803                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38804                 }
38805                 return true;
38806             },
38807
38808             forceKeyDown: true
38809         });
38810         this.queryDelay = Math.max(this.queryDelay || 10,
38811                 this.mode == 'local' ? 10 : 250);
38812         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
38813         if(this.typeAhead){
38814             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
38815         }
38816         if(this.editable !== false){
38817             this.el.on("keyup", this.onKeyUp, this);
38818         }
38819         if(this.forceSelection){
38820             this.on('blur', this.doForce, this);
38821         }
38822     },
38823
38824     onDestroy : function(){
38825         if(this.view){
38826             this.view.setStore(null);
38827             this.view.el.removeAllListeners();
38828             this.view.el.remove();
38829             this.view.purgeListeners();
38830         }
38831         if(this.list){
38832             this.list.destroy();
38833         }
38834         if(this.store){
38835             this.store.un('beforeload', this.onBeforeLoad, this);
38836             this.store.un('load', this.onLoad, this);
38837             this.store.un('loadexception', this.onLoadException, this);
38838         }
38839         Roo.form.ComboBox.superclass.onDestroy.call(this);
38840     },
38841
38842     // private
38843     fireKey : function(e){
38844         if(e.isNavKeyPress() && !this.list.isVisible()){
38845             this.fireEvent("specialkey", this, e);
38846         }
38847     },
38848
38849     // private
38850     onResize: function(w, h){
38851         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
38852         
38853         if(typeof w != 'number'){
38854             // we do not handle it!?!?
38855             return;
38856         }
38857         var tw = this.trigger.getWidth();
38858         tw += this.addicon ? this.addicon.getWidth() : 0;
38859         tw += this.editicon ? this.editicon.getWidth() : 0;
38860         var x = w - tw;
38861         this.el.setWidth( this.adjustWidth('input', x));
38862             
38863         this.trigger.setStyle('left', x+'px');
38864         
38865         if(this.list && this.listWidth === undefined){
38866             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
38867             this.list.setWidth(lw);
38868             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38869         }
38870         
38871     
38872         
38873     },
38874
38875     /**
38876      * Allow or prevent the user from directly editing the field text.  If false is passed,
38877      * the user will only be able to select from the items defined in the dropdown list.  This method
38878      * is the runtime equivalent of setting the 'editable' config option at config time.
38879      * @param {Boolean} value True to allow the user to directly edit the field text
38880      */
38881     setEditable : function(value){
38882         if(value == this.editable){
38883             return;
38884         }
38885         this.editable = value;
38886         if(!value){
38887             this.el.dom.setAttribute('readOnly', true);
38888             this.el.on('mousedown', this.onTriggerClick,  this);
38889             this.el.addClass('x-combo-noedit');
38890         }else{
38891             this.el.dom.setAttribute('readOnly', false);
38892             this.el.un('mousedown', this.onTriggerClick,  this);
38893             this.el.removeClass('x-combo-noedit');
38894         }
38895     },
38896
38897     // private
38898     onBeforeLoad : function(){
38899         if(!this.hasFocus){
38900             return;
38901         }
38902         this.innerList.update(this.loadingText ?
38903                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
38904         this.restrictHeight();
38905         this.selectedIndex = -1;
38906     },
38907
38908     // private
38909     onLoad : function(){
38910         if(!this.hasFocus){
38911             return;
38912         }
38913         if(this.store.getCount() > 0){
38914             this.expand();
38915             this.restrictHeight();
38916             if(this.lastQuery == this.allQuery){
38917                 if(this.editable){
38918                     this.el.dom.select();
38919                 }
38920                 if(!this.selectByValue(this.value, true)){
38921                     this.select(0, true);
38922                 }
38923             }else{
38924                 this.selectNext();
38925                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
38926                     this.taTask.delay(this.typeAheadDelay);
38927                 }
38928             }
38929         }else{
38930             this.onEmptyResults();
38931         }
38932         //this.el.focus();
38933     },
38934     // private
38935     onLoadException : function()
38936     {
38937         this.collapse();
38938         Roo.log(this.store.reader.jsonData);
38939         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38940             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38941         }
38942         
38943         
38944     },
38945     // private
38946     onTypeAhead : function(){
38947         if(this.store.getCount() > 0){
38948             var r = this.store.getAt(0);
38949             var newValue = r.data[this.displayField];
38950             var len = newValue.length;
38951             var selStart = this.getRawValue().length;
38952             if(selStart != len){
38953                 this.setRawValue(newValue);
38954                 this.selectText(selStart, newValue.length);
38955             }
38956         }
38957     },
38958
38959     // private
38960     onSelect : function(record, index){
38961         if(this.fireEvent('beforeselect', this, record, index) !== false){
38962             this.setFromData(index > -1 ? record.data : false);
38963             this.collapse();
38964             this.fireEvent('select', this, record, index);
38965         }
38966     },
38967
38968     /**
38969      * Returns the currently selected field value or empty string if no value is set.
38970      * @return {String} value The selected value
38971      */
38972     getValue : function(){
38973         if(this.valueField){
38974             return typeof this.value != 'undefined' ? this.value : '';
38975         }else{
38976             return Roo.form.ComboBox.superclass.getValue.call(this);
38977         }
38978     },
38979
38980     /**
38981      * Clears any text/value currently set in the field
38982      */
38983     clearValue : function(){
38984         if(this.hiddenField){
38985             this.hiddenField.value = '';
38986         }
38987         this.value = '';
38988         this.setRawValue('');
38989         this.lastSelectionText = '';
38990         
38991     },
38992
38993     /**
38994      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
38995      * will be displayed in the field.  If the value does not match the data value of an existing item,
38996      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
38997      * Otherwise the field will be blank (although the value will still be set).
38998      * @param {String} value The value to match
38999      */
39000     setValue : function(v){
39001         var text = v;
39002         if(this.valueField){
39003             var r = this.findRecord(this.valueField, v);
39004             if(r){
39005                 text = r.data[this.displayField];
39006             }else if(this.valueNotFoundText !== undefined){
39007                 text = this.valueNotFoundText;
39008             }
39009         }
39010         this.lastSelectionText = text;
39011         if(this.hiddenField){
39012             this.hiddenField.value = v;
39013         }
39014         Roo.form.ComboBox.superclass.setValue.call(this, text);
39015         this.value = v;
39016     },
39017     /**
39018      * @property {Object} the last set data for the element
39019      */
39020     
39021     lastData : false,
39022     /**
39023      * Sets the value of the field based on a object which is related to the record format for the store.
39024      * @param {Object} value the value to set as. or false on reset?
39025      */
39026     setFromData : function(o){
39027         var dv = ''; // display value
39028         var vv = ''; // value value..
39029         this.lastData = o;
39030         if (this.displayField) {
39031             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39032         } else {
39033             // this is an error condition!!!
39034             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39035         }
39036         
39037         if(this.valueField){
39038             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39039         }
39040         if(this.hiddenField){
39041             this.hiddenField.value = vv;
39042             
39043             this.lastSelectionText = dv;
39044             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39045             this.value = vv;
39046             return;
39047         }
39048         // no hidden field.. - we store the value in 'value', but still display
39049         // display field!!!!
39050         this.lastSelectionText = dv;
39051         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39052         this.value = vv;
39053         
39054         
39055     },
39056     // private
39057     reset : function(){
39058         // overridden so that last data is reset..
39059         this.setValue(this.originalValue);
39060         this.clearInvalid();
39061         this.lastData = false;
39062         if (this.view) {
39063             this.view.clearSelections();
39064         }
39065     },
39066     // private
39067     findRecord : function(prop, value){
39068         var record;
39069         if(this.store.getCount() > 0){
39070             this.store.each(function(r){
39071                 if(r.data[prop] == value){
39072                     record = r;
39073                     return false;
39074                 }
39075                 return true;
39076             });
39077         }
39078         return record;
39079     },
39080     
39081     getName: function()
39082     {
39083         // returns hidden if it's set..
39084         if (!this.rendered) {return ''};
39085         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39086         
39087     },
39088     // private
39089     onViewMove : function(e, t){
39090         this.inKeyMode = false;
39091     },
39092
39093     // private
39094     onViewOver : function(e, t){
39095         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39096             return;
39097         }
39098         var item = this.view.findItemFromChild(t);
39099         if(item){
39100             var index = this.view.indexOf(item);
39101             this.select(index, false);
39102         }
39103     },
39104
39105     // private
39106     onViewClick : function(doFocus)
39107     {
39108         var index = this.view.getSelectedIndexes()[0];
39109         var r = this.store.getAt(index);
39110         if(r){
39111             this.onSelect(r, index);
39112         }
39113         if(doFocus !== false && !this.blockFocus){
39114             this.el.focus();
39115         }
39116     },
39117
39118     // private
39119     restrictHeight : function(){
39120         this.innerList.dom.style.height = '';
39121         var inner = this.innerList.dom;
39122         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39123         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39124         this.list.beginUpdate();
39125         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39126         this.list.alignTo(this.el, this.listAlign);
39127         this.list.endUpdate();
39128     },
39129
39130     // private
39131     onEmptyResults : function(){
39132         this.collapse();
39133     },
39134
39135     /**
39136      * Returns true if the dropdown list is expanded, else false.
39137      */
39138     isExpanded : function(){
39139         return this.list.isVisible();
39140     },
39141
39142     /**
39143      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39144      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39145      * @param {String} value The data value of the item to select
39146      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39147      * selected item if it is not currently in view (defaults to true)
39148      * @return {Boolean} True if the value matched an item in the list, else false
39149      */
39150     selectByValue : function(v, scrollIntoView){
39151         if(v !== undefined && v !== null){
39152             var r = this.findRecord(this.valueField || this.displayField, v);
39153             if(r){
39154                 this.select(this.store.indexOf(r), scrollIntoView);
39155                 return true;
39156             }
39157         }
39158         return false;
39159     },
39160
39161     /**
39162      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39163      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39164      * @param {Number} index The zero-based index of the list item to select
39165      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39166      * selected item if it is not currently in view (defaults to true)
39167      */
39168     select : function(index, scrollIntoView){
39169         this.selectedIndex = index;
39170         this.view.select(index);
39171         if(scrollIntoView !== false){
39172             var el = this.view.getNode(index);
39173             if(el){
39174                 this.innerList.scrollChildIntoView(el, false);
39175             }
39176         }
39177     },
39178
39179     // private
39180     selectNext : function(){
39181         var ct = this.store.getCount();
39182         if(ct > 0){
39183             if(this.selectedIndex == -1){
39184                 this.select(0);
39185             }else if(this.selectedIndex < ct-1){
39186                 this.select(this.selectedIndex+1);
39187             }
39188         }
39189     },
39190
39191     // private
39192     selectPrev : function(){
39193         var ct = this.store.getCount();
39194         if(ct > 0){
39195             if(this.selectedIndex == -1){
39196                 this.select(0);
39197             }else if(this.selectedIndex != 0){
39198                 this.select(this.selectedIndex-1);
39199             }
39200         }
39201     },
39202
39203     // private
39204     onKeyUp : function(e){
39205         if(this.editable !== false && !e.isSpecialKey()){
39206             this.lastKey = e.getKey();
39207             this.dqTask.delay(this.queryDelay);
39208         }
39209     },
39210
39211     // private
39212     validateBlur : function(){
39213         return !this.list || !this.list.isVisible();   
39214     },
39215
39216     // private
39217     initQuery : function(){
39218         this.doQuery(this.getRawValue());
39219     },
39220
39221     // private
39222     doForce : function(){
39223         if(this.el.dom.value.length > 0){
39224             this.el.dom.value =
39225                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39226              
39227         }
39228     },
39229
39230     /**
39231      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39232      * query allowing the query action to be canceled if needed.
39233      * @param {String} query The SQL query to execute
39234      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39235      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39236      * saved in the current store (defaults to false)
39237      */
39238     doQuery : function(q, forceAll){
39239         if(q === undefined || q === null){
39240             q = '';
39241         }
39242         var qe = {
39243             query: q,
39244             forceAll: forceAll,
39245             combo: this,
39246             cancel:false
39247         };
39248         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39249             return false;
39250         }
39251         q = qe.query;
39252         forceAll = qe.forceAll;
39253         if(forceAll === true || (q.length >= this.minChars)){
39254             if(this.lastQuery != q || this.alwaysQuery){
39255                 this.lastQuery = q;
39256                 if(this.mode == 'local'){
39257                     this.selectedIndex = -1;
39258                     if(forceAll){
39259                         this.store.clearFilter();
39260                     }else{
39261                         this.store.filter(this.displayField, q);
39262                     }
39263                     this.onLoad();
39264                 }else{
39265                     this.store.baseParams[this.queryParam] = q;
39266                     this.store.load({
39267                         params: this.getParams(q)
39268                     });
39269                     this.expand();
39270                 }
39271             }else{
39272                 this.selectedIndex = -1;
39273                 this.onLoad();   
39274             }
39275         }
39276     },
39277
39278     // private
39279     getParams : function(q){
39280         var p = {};
39281         //p[this.queryParam] = q;
39282         if(this.pageSize){
39283             p.start = 0;
39284             p.limit = this.pageSize;
39285         }
39286         return p;
39287     },
39288
39289     /**
39290      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39291      */
39292     collapse : function(){
39293         if(!this.isExpanded()){
39294             return;
39295         }
39296         this.list.hide();
39297         Roo.get(document).un('mousedown', this.collapseIf, this);
39298         Roo.get(document).un('mousewheel', this.collapseIf, this);
39299         if (!this.editable) {
39300             Roo.get(document).un('keydown', this.listKeyPress, this);
39301         }
39302         this.fireEvent('collapse', this);
39303     },
39304
39305     // private
39306     collapseIf : function(e){
39307         if(!e.within(this.wrap) && !e.within(this.list)){
39308             this.collapse();
39309         }
39310     },
39311
39312     /**
39313      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39314      */
39315     expand : function(){
39316         if(this.isExpanded() || !this.hasFocus){
39317             return;
39318         }
39319         this.list.alignTo(this.el, this.listAlign);
39320         this.list.show();
39321         Roo.get(document).on('mousedown', this.collapseIf, this);
39322         Roo.get(document).on('mousewheel', this.collapseIf, this);
39323         if (!this.editable) {
39324             Roo.get(document).on('keydown', this.listKeyPress, this);
39325         }
39326         
39327         this.fireEvent('expand', this);
39328     },
39329
39330     // private
39331     // Implements the default empty TriggerField.onTriggerClick function
39332     onTriggerClick : function(){
39333         if(this.disabled){
39334             return;
39335         }
39336         if(this.isExpanded()){
39337             this.collapse();
39338             if (!this.blockFocus) {
39339                 this.el.focus();
39340             }
39341             
39342         }else {
39343             this.hasFocus = true;
39344             if(this.triggerAction == 'all') {
39345                 this.doQuery(this.allQuery, true);
39346             } else {
39347                 this.doQuery(this.getRawValue());
39348             }
39349             if (!this.blockFocus) {
39350                 this.el.focus();
39351             }
39352         }
39353     },
39354     listKeyPress : function(e)
39355     {
39356         //Roo.log('listkeypress');
39357         // scroll to first matching element based on key pres..
39358         if (e.isSpecialKey()) {
39359             return false;
39360         }
39361         var k = String.fromCharCode(e.getKey()).toUpperCase();
39362         //Roo.log(k);
39363         var match  = false;
39364         var csel = this.view.getSelectedNodes();
39365         var cselitem = false;
39366         if (csel.length) {
39367             var ix = this.view.indexOf(csel[0]);
39368             cselitem  = this.store.getAt(ix);
39369             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
39370                 cselitem = false;
39371             }
39372             
39373         }
39374         
39375         this.store.each(function(v) { 
39376             if (cselitem) {
39377                 // start at existing selection.
39378                 if (cselitem.id == v.id) {
39379                     cselitem = false;
39380                 }
39381                 return;
39382             }
39383                 
39384             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
39385                 match = this.store.indexOf(v);
39386                 return false;
39387             }
39388         }, this);
39389         
39390         if (match === false) {
39391             return true; // no more action?
39392         }
39393         // scroll to?
39394         this.view.select(match);
39395         var sn = Roo.get(this.view.getSelectedNodes()[0])
39396         sn.scrollIntoView(sn.dom.parentNode, false);
39397     }
39398
39399     /** 
39400     * @cfg {Boolean} grow 
39401     * @hide 
39402     */
39403     /** 
39404     * @cfg {Number} growMin 
39405     * @hide 
39406     */
39407     /** 
39408     * @cfg {Number} growMax 
39409     * @hide 
39410     */
39411     /**
39412      * @hide
39413      * @method autoSize
39414      */
39415 });/*
39416  * Copyright(c) 2010-2012, Roo J Solutions Limited
39417  *
39418  * Licence LGPL
39419  *
39420  */
39421
39422 /**
39423  * @class Roo.form.ComboBoxArray
39424  * @extends Roo.form.TextField
39425  * A facebook style adder... for lists of email / people / countries  etc...
39426  * pick multiple items from a combo box, and shows each one.
39427  *
39428  *  Fred [x]  Brian [x]  [Pick another |v]
39429  *
39430  *
39431  *  For this to work: it needs various extra information
39432  *    - normal combo problay has
39433  *      name, hiddenName
39434  *    + displayField, valueField
39435  *
39436  *    For our purpose...
39437  *
39438  *
39439  *   If we change from 'extends' to wrapping...
39440  *   
39441  *  
39442  *
39443  
39444  
39445  * @constructor
39446  * Create a new ComboBoxArray.
39447  * @param {Object} config Configuration options
39448  */
39449  
39450
39451 Roo.form.ComboBoxArray = function(config)
39452 {
39453     
39454     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
39455     
39456     this.items = new Roo.util.MixedCollection(false);
39457     
39458     // construct the child combo...
39459     
39460     
39461     
39462     
39463    
39464     
39465 }
39466
39467  
39468 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
39469
39470     /**
39471      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
39472      */
39473     
39474     lastData : false,
39475     
39476     // behavies liek a hiddne field
39477     inputType:      'hidden',
39478     /**
39479      * @cfg {Number} width The width of the box that displays the selected element
39480      */ 
39481     width:          300,
39482
39483     
39484     
39485     /**
39486      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
39487      */
39488     name : false,
39489     /**
39490      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
39491      */
39492     hiddenName : false,
39493     
39494     
39495     // private the array of items that are displayed..
39496     items  : false,
39497     // private - the hidden field el.
39498     hiddenEl : false,
39499     // private - the filed el..
39500     el : false,
39501     
39502     //validateValue : function() { return true; }, // all values are ok!
39503     //onAddClick: function() { },
39504     
39505     onRender : function(ct, position) 
39506     {
39507         
39508         // create the standard hidden element
39509         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
39510         
39511         
39512         // give fake names to child combo;
39513         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
39514         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
39515         
39516         this.combo = Roo.factory(this.combo, Roo.form);
39517         this.combo.onRender(ct, position);
39518         this.combo.onResize(this.combo.width,0);
39519         this.combo.initEvents();
39520         
39521         // assigned so form know we need to do this..
39522         this.store          = this.combo.store;
39523         this.valueField     = this.combo.valueField;
39524         this.displayField   = this.combo.displayField ;
39525         
39526         
39527         this.combo.wrap.addClass('x-cbarray-grp');
39528         
39529         var cbwrap = this.combo.wrap.createChild(
39530             {tag: 'div', cls: 'x-cbarray-cb'},
39531             this.combo.el.dom
39532         );
39533         
39534              
39535         this.hiddenEl = this.combo.wrap.createChild({
39536             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
39537         });
39538         this.el = this.combo.wrap.createChild({
39539             tag: 'input',  type:'hidden' , name: this.name, value : ''
39540         });
39541          //   this.el.dom.removeAttribute("name");
39542         
39543         
39544         this.outerWrap = this.combo.wrap;
39545         this.wrap = cbwrap;
39546         
39547         this.outerWrap.setWidth(this.width);
39548         this.outerWrap.dom.removeChild(this.el.dom);
39549         
39550         this.wrap.dom.appendChild(this.el.dom);
39551         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
39552         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
39553         
39554         this.combo.trigger.setStyle('position','relative');
39555         this.combo.trigger.setStyle('left', '0px');
39556         this.combo.trigger.setStyle('top', '2px');
39557         
39558         this.combo.el.setStyle('vertical-align', 'text-bottom');
39559         
39560         //this.trigger.setStyle('vertical-align', 'top');
39561         
39562         // this should use the code from combo really... on('add' ....)
39563         if (this.adder) {
39564             
39565         
39566             this.adder = this.outerWrap.createChild(
39567                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
39568             var _t = this;
39569             this.adder.on('click', function(e) {
39570                 _t.fireEvent('adderclick', this, e);
39571             }, _t);
39572         }
39573         //var _t = this;
39574         //this.adder.on('click', this.onAddClick, _t);
39575         
39576         
39577         this.combo.on('select', function(cb, rec, ix) {
39578             this.addItem(rec.data);
39579             
39580             cb.setValue('');
39581             cb.el.dom.value = '';
39582             //cb.lastData = rec.data;
39583             // add to list
39584             
39585         }, this);
39586         
39587         
39588     },
39589     
39590     
39591     getName: function()
39592     {
39593         // returns hidden if it's set..
39594         if (!this.rendered) {return ''};
39595         return  this.hiddenName ? this.hiddenName : this.name;
39596         
39597     },
39598     
39599     
39600     onResize: function(w, h){
39601         
39602         return;
39603         // not sure if this is needed..
39604         //this.combo.onResize(w,h);
39605         
39606         if(typeof w != 'number'){
39607             // we do not handle it!?!?
39608             return;
39609         }
39610         var tw = this.combo.trigger.getWidth();
39611         tw += this.addicon ? this.addicon.getWidth() : 0;
39612         tw += this.editicon ? this.editicon.getWidth() : 0;
39613         var x = w - tw;
39614         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
39615             
39616         this.combo.trigger.setStyle('left', '0px');
39617         
39618         if(this.list && this.listWidth === undefined){
39619             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
39620             this.list.setWidth(lw);
39621             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39622         }
39623         
39624     
39625         
39626     },
39627     
39628     addItem: function(rec)
39629     {
39630         var valueField = this.combo.valueField;
39631         var displayField = this.combo.displayField;
39632         if (this.items.indexOfKey(rec[valueField]) > -1) {
39633             //console.log("GOT " + rec.data.id);
39634             return;
39635         }
39636         
39637         var x = new Roo.form.ComboBoxArray.Item({
39638             //id : rec[this.idField],
39639             data : rec,
39640             displayField : displayField ,
39641             tipField : displayField ,
39642             cb : this
39643         });
39644         // use the 
39645         this.items.add(rec[valueField],x);
39646         // add it before the element..
39647         this.updateHiddenEl();
39648         x.render(this.outerWrap, this.wrap.dom);
39649         // add the image handler..
39650     },
39651     
39652     updateHiddenEl : function()
39653     {
39654         this.validate();
39655         if (!this.hiddenEl) {
39656             return;
39657         }
39658         var ar = [];
39659         var idField = this.combo.valueField;
39660         
39661         this.items.each(function(f) {
39662             ar.push(f.data[idField]);
39663            
39664         });
39665         this.hiddenEl.dom.value = ar.join(',');
39666         this.validate();
39667     },
39668     
39669     reset : function()
39670     {
39671         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
39672         this.items.each(function(f) {
39673            f.remove(); 
39674         });
39675         this.el.dom.value = '';
39676         if (this.hiddenEl) {
39677             this.hiddenEl.dom.value = '';
39678         }
39679         
39680     },
39681     getValue: function()
39682     {
39683         return this.hiddenEl ? this.hiddenEl.dom.value : '';
39684     },
39685     setValue: function(v) // not a valid action - must use addItems..
39686     {
39687          
39688         this.reset();
39689         
39690         
39691         
39692         if (this.store.isLocal && (typeof(v) == 'string')) {
39693             // then we can use the store to find the values..
39694             // comma seperated at present.. this needs to allow JSON based encoding..
39695             this.hiddenEl.value  = v;
39696             var v_ar = [];
39697             Roo.each(v.split(','), function(k) {
39698                 Roo.log("CHECK " + this.valueField + ',' + k);
39699                 var li = this.store.query(this.valueField, k);
39700                 if (!li.length) {
39701                     return;
39702                 }
39703                 add = {};
39704                 add[this.valueField] = k;
39705                 add[this.displayField] = li.item(0).data[this.displayField];
39706                 
39707                 this.addItem(add);
39708             }, this) 
39709              
39710         }
39711         if (typeof(v) == 'object') {
39712             // then let's assume it's an array of objects..
39713             Roo.each(v, function(l) {
39714                 this.addItem(l);
39715             }, this);
39716              
39717         }
39718         
39719         
39720     },
39721     setFromData: function(v)
39722     {
39723         // this recieves an object, if setValues is called.
39724         this.reset();
39725         this.el.dom.value = v[this.displayField];
39726         this.hiddenEl.dom.value = v[this.valueField];
39727         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
39728             return;
39729         }
39730         var kv = v[this.valueField];
39731         var dv = v[this.displayField];
39732         kv = typeof(kv) != 'string' ? '' : kv;
39733         dv = typeof(dv) != 'string' ? '' : dv;
39734         
39735         
39736         var keys = kv.split(',');
39737         var display = dv.split(',');
39738         for (var i = 0 ; i < keys.length; i++) {
39739             
39740             add = {};
39741             add[this.valueField] = keys[i];
39742             add[this.displayField] = display[i];
39743             this.addItem(add);
39744         }
39745       
39746         
39747     },
39748     
39749     
39750     validateValue : function(value){
39751         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
39752         
39753     }
39754     
39755 });
39756
39757
39758
39759 /**
39760  * @class Roo.form.ComboBoxArray.Item
39761  * @extends Roo.BoxComponent
39762  * A selected item in the list
39763  *  Fred [x]  Brian [x]  [Pick another |v]
39764  * 
39765  * @constructor
39766  * Create a new item.
39767  * @param {Object} config Configuration options
39768  */
39769  
39770 Roo.form.ComboBoxArray.Item = function(config) {
39771     config.id = Roo.id();
39772     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
39773 }
39774
39775 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
39776     data : {},
39777     cb: false,
39778     displayField : false,
39779     tipField : false,
39780     
39781     
39782     defaultAutoCreate : {
39783         tag: 'div',
39784         cls: 'x-cbarray-item',
39785         cn : [ 
39786             { tag: 'div' },
39787             {
39788                 tag: 'img',
39789                 width:16,
39790                 height : 16,
39791                 src : Roo.BLANK_IMAGE_URL ,
39792                 align: 'center'
39793             }
39794         ]
39795         
39796     },
39797     
39798  
39799     onRender : function(ct, position)
39800     {
39801         Roo.form.Field.superclass.onRender.call(this, ct, position);
39802         
39803         if(!this.el){
39804             var cfg = this.getAutoCreate();
39805             this.el = ct.createChild(cfg, position);
39806         }
39807         
39808         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
39809         
39810         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
39811             this.cb.renderer(this.data) :
39812             String.format('{0}',this.data[this.displayField]);
39813         
39814             
39815         this.el.child('div').dom.setAttribute('qtip',
39816                         String.format('{0}',this.data[this.tipField])
39817         );
39818         
39819         this.el.child('img').on('click', this.remove, this);
39820         
39821     },
39822    
39823     remove : function()
39824     {
39825         
39826         this.cb.items.remove(this);
39827         this.el.child('img').un('click', this.remove, this);
39828         this.el.remove();
39829         this.cb.updateHiddenEl();
39830     }
39831     
39832     
39833 });/*
39834  * Based on:
39835  * Ext JS Library 1.1.1
39836  * Copyright(c) 2006-2007, Ext JS, LLC.
39837  *
39838  * Originally Released Under LGPL - original licence link has changed is not relivant.
39839  *
39840  * Fork - LGPL
39841  * <script type="text/javascript">
39842  */
39843 /**
39844  * @class Roo.form.Checkbox
39845  * @extends Roo.form.Field
39846  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
39847  * @constructor
39848  * Creates a new Checkbox
39849  * @param {Object} config Configuration options
39850  */
39851 Roo.form.Checkbox = function(config){
39852     Roo.form.Checkbox.superclass.constructor.call(this, config);
39853     this.addEvents({
39854         /**
39855          * @event check
39856          * Fires when the checkbox is checked or unchecked.
39857              * @param {Roo.form.Checkbox} this This checkbox
39858              * @param {Boolean} checked The new checked value
39859              */
39860         check : true
39861     });
39862 };
39863
39864 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
39865     /**
39866      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
39867      */
39868     focusClass : undefined,
39869     /**
39870      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
39871      */
39872     fieldClass: "x-form-field",
39873     /**
39874      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
39875      */
39876     checked: false,
39877     /**
39878      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39879      * {tag: "input", type: "checkbox", autocomplete: "off"})
39880      */
39881     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
39882     /**
39883      * @cfg {String} boxLabel The text that appears beside the checkbox
39884      */
39885     boxLabel : "",
39886     /**
39887      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
39888      */  
39889     inputValue : '1',
39890     /**
39891      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
39892      */
39893      valueOff: '0', // value when not checked..
39894
39895     actionMode : 'viewEl', 
39896     //
39897     // private
39898     itemCls : 'x-menu-check-item x-form-item',
39899     groupClass : 'x-menu-group-item',
39900     inputType : 'hidden',
39901     
39902     
39903     inSetChecked: false, // check that we are not calling self...
39904     
39905     inputElement: false, // real input element?
39906     basedOn: false, // ????
39907     
39908     isFormField: true, // not sure where this is needed!!!!
39909
39910     onResize : function(){
39911         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
39912         if(!this.boxLabel){
39913             this.el.alignTo(this.wrap, 'c-c');
39914         }
39915     },
39916
39917     initEvents : function(){
39918         Roo.form.Checkbox.superclass.initEvents.call(this);
39919         this.el.on("click", this.onClick,  this);
39920         this.el.on("change", this.onClick,  this);
39921     },
39922
39923
39924     getResizeEl : function(){
39925         return this.wrap;
39926     },
39927
39928     getPositionEl : function(){
39929         return this.wrap;
39930     },
39931
39932     // private
39933     onRender : function(ct, position){
39934         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
39935         /*
39936         if(this.inputValue !== undefined){
39937             this.el.dom.value = this.inputValue;
39938         }
39939         */
39940         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
39941         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
39942         var viewEl = this.wrap.createChild({ 
39943             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
39944         this.viewEl = viewEl;   
39945         this.wrap.on('click', this.onClick,  this); 
39946         
39947         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
39948         this.el.on('propertychange', this.setFromHidden,  this);  //ie
39949         
39950         
39951         
39952         if(this.boxLabel){
39953             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
39954         //    viewEl.on('click', this.onClick,  this); 
39955         }
39956         //if(this.checked){
39957             this.setChecked(this.checked);
39958         //}else{
39959             //this.checked = this.el.dom;
39960         //}
39961
39962     },
39963
39964     // private
39965     initValue : Roo.emptyFn,
39966
39967     /**
39968      * Returns the checked state of the checkbox.
39969      * @return {Boolean} True if checked, else false
39970      */
39971     getValue : function(){
39972         if(this.el){
39973             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
39974         }
39975         return this.valueOff;
39976         
39977     },
39978
39979         // private
39980     onClick : function(){ 
39981         this.setChecked(!this.checked);
39982
39983         //if(this.el.dom.checked != this.checked){
39984         //    this.setValue(this.el.dom.checked);
39985        // }
39986     },
39987
39988     /**
39989      * Sets the checked state of the checkbox.
39990      * On is always based on a string comparison between inputValue and the param.
39991      * @param {Boolean/String} value - the value to set 
39992      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
39993      */
39994     setValue : function(v,suppressEvent){
39995         
39996         
39997         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
39998         //if(this.el && this.el.dom){
39999         //    this.el.dom.checked = this.checked;
40000         //    this.el.dom.defaultChecked = this.checked;
40001         //}
40002         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40003         //this.fireEvent("check", this, this.checked);
40004     },
40005     // private..
40006     setChecked : function(state,suppressEvent)
40007     {
40008         if (this.inSetChecked) {
40009             this.checked = state;
40010             return;
40011         }
40012         
40013     
40014         if(this.wrap){
40015             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40016         }
40017         this.checked = state;
40018         if(suppressEvent !== true){
40019             this.fireEvent('check', this, state);
40020         }
40021         this.inSetChecked = true;
40022         this.el.dom.value = state ? this.inputValue : this.valueOff;
40023         this.inSetChecked = false;
40024         
40025     },
40026     // handle setting of hidden value by some other method!!?!?
40027     setFromHidden: function()
40028     {
40029         if(!this.el){
40030             return;
40031         }
40032         //console.log("SET FROM HIDDEN");
40033         //alert('setFrom hidden');
40034         this.setValue(this.el.dom.value);
40035     },
40036     
40037     onDestroy : function()
40038     {
40039         if(this.viewEl){
40040             Roo.get(this.viewEl).remove();
40041         }
40042          
40043         Roo.form.Checkbox.superclass.onDestroy.call(this);
40044     }
40045
40046 });/*
40047  * Based on:
40048  * Ext JS Library 1.1.1
40049  * Copyright(c) 2006-2007, Ext JS, LLC.
40050  *
40051  * Originally Released Under LGPL - original licence link has changed is not relivant.
40052  *
40053  * Fork - LGPL
40054  * <script type="text/javascript">
40055  */
40056  
40057 /**
40058  * @class Roo.form.Radio
40059  * @extends Roo.form.Checkbox
40060  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40061  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40062  * @constructor
40063  * Creates a new Radio
40064  * @param {Object} config Configuration options
40065  */
40066 Roo.form.Radio = function(){
40067     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40068 };
40069 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40070     inputType: 'radio',
40071
40072     /**
40073      * If this radio is part of a group, it will return the selected value
40074      * @return {String}
40075      */
40076     getGroupValue : function(){
40077         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40078     }
40079 });//<script type="text/javascript">
40080
40081 /*
40082  * Ext JS Library 1.1.1
40083  * Copyright(c) 2006-2007, Ext JS, LLC.
40084  * licensing@extjs.com
40085  * 
40086  * http://www.extjs.com/license
40087  */
40088  
40089  /*
40090   * 
40091   * Known bugs:
40092   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40093   * - IE ? - no idea how much works there.
40094   * 
40095   * 
40096   * 
40097   */
40098  
40099
40100 /**
40101  * @class Ext.form.HtmlEditor
40102  * @extends Ext.form.Field
40103  * Provides a lightweight HTML Editor component.
40104  *
40105  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40106  * 
40107  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40108  * supported by this editor.</b><br/><br/>
40109  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40110  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40111  */
40112 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40113       /**
40114      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40115      */
40116     toolbars : false,
40117     /**
40118      * @cfg {String} createLinkText The default text for the create link prompt
40119      */
40120     createLinkText : 'Please enter the URL for the link:',
40121     /**
40122      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40123      */
40124     defaultLinkValue : 'http:/'+'/',
40125    
40126      /**
40127      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40128      *                        Roo.resizable.
40129      */
40130     resizable : false,
40131      /**
40132      * @cfg {Number} height (in pixels)
40133      */   
40134     height: 300,
40135    /**
40136      * @cfg {Number} width (in pixels)
40137      */   
40138     width: 500,
40139     
40140     /**
40141      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40142      * 
40143      */
40144     stylesheets: false,
40145     
40146     // id of frame..
40147     frameId: false,
40148     
40149     // private properties
40150     validationEvent : false,
40151     deferHeight: true,
40152     initialized : false,
40153     activated : false,
40154     sourceEditMode : false,
40155     onFocus : Roo.emptyFn,
40156     iframePad:3,
40157     hideMode:'offsets',
40158     
40159     defaultAutoCreate : { // modified by initCompnoent..
40160         tag: "textarea",
40161         style:"width:500px;height:300px;",
40162         autocomplete: "off"
40163     },
40164
40165     // private
40166     initComponent : function(){
40167         this.addEvents({
40168             /**
40169              * @event initialize
40170              * Fires when the editor is fully initialized (including the iframe)
40171              * @param {HtmlEditor} this
40172              */
40173             initialize: true,
40174             /**
40175              * @event activate
40176              * Fires when the editor is first receives the focus. Any insertion must wait
40177              * until after this event.
40178              * @param {HtmlEditor} this
40179              */
40180             activate: true,
40181              /**
40182              * @event beforesync
40183              * Fires before the textarea is updated with content from the editor iframe. Return false
40184              * to cancel the sync.
40185              * @param {HtmlEditor} this
40186              * @param {String} html
40187              */
40188             beforesync: true,
40189              /**
40190              * @event beforepush
40191              * Fires before the iframe editor is updated with content from the textarea. Return false
40192              * to cancel the push.
40193              * @param {HtmlEditor} this
40194              * @param {String} html
40195              */
40196             beforepush: true,
40197              /**
40198              * @event sync
40199              * Fires when the textarea is updated with content from the editor iframe.
40200              * @param {HtmlEditor} this
40201              * @param {String} html
40202              */
40203             sync: true,
40204              /**
40205              * @event push
40206              * Fires when the iframe editor is updated with content from the textarea.
40207              * @param {HtmlEditor} this
40208              * @param {String} html
40209              */
40210             push: true,
40211              /**
40212              * @event editmodechange
40213              * Fires when the editor switches edit modes
40214              * @param {HtmlEditor} this
40215              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40216              */
40217             editmodechange: true,
40218             /**
40219              * @event editorevent
40220              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40221              * @param {HtmlEditor} this
40222              */
40223             editorevent: true
40224         });
40225         this.defaultAutoCreate =  {
40226             tag: "textarea",
40227             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40228             autocomplete: "off"
40229         };
40230     },
40231
40232     /**
40233      * Protected method that will not generally be called directly. It
40234      * is called when the editor creates its toolbar. Override this method if you need to
40235      * add custom toolbar buttons.
40236      * @param {HtmlEditor} editor
40237      */
40238     createToolbar : function(editor){
40239         if (!editor.toolbars || !editor.toolbars.length) {
40240             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40241         }
40242         
40243         for (var i =0 ; i < editor.toolbars.length;i++) {
40244             editor.toolbars[i] = Roo.factory(
40245                     typeof(editor.toolbars[i]) == 'string' ?
40246                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
40247                 Roo.form.HtmlEditor);
40248             editor.toolbars[i].init(editor);
40249         }
40250          
40251         
40252     },
40253
40254     /**
40255      * Protected method that will not generally be called directly. It
40256      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40257      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40258      */
40259     getDocMarkup : function(){
40260         // body styles..
40261         var st = '';
40262         if (this.stylesheets === false) {
40263             
40264             Roo.get(document.head).select('style').each(function(node) {
40265                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40266             });
40267             
40268             Roo.get(document.head).select('link').each(function(node) { 
40269                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40270             });
40271             
40272         } else if (!this.stylesheets.length) {
40273                 // simple..
40274                 st = '<style type="text/css">' +
40275                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40276                    '</style>';
40277         } else {
40278             Roo.each(this.stylesheets, function(s) {
40279                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
40280             });
40281             
40282         }
40283         
40284         st +=  '<style type="text/css">' +
40285             'IMG { cursor: pointer } ' +
40286         '</style>';
40287
40288         
40289         return '<html><head>' + st  +
40290             //<style type="text/css">' +
40291             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40292             //'</style>' +
40293             ' </head><body class="roo-htmleditor-body"></body></html>';
40294     },
40295
40296     // private
40297     onRender : function(ct, position)
40298     {
40299         var _t = this;
40300         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
40301         this.el.dom.style.border = '0 none';
40302         this.el.dom.setAttribute('tabIndex', -1);
40303         this.el.addClass('x-hidden');
40304         if(Roo.isIE){ // fix IE 1px bogus margin
40305             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
40306         }
40307         this.wrap = this.el.wrap({
40308             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
40309         });
40310         
40311         if (this.resizable) {
40312             this.resizeEl = new Roo.Resizable(this.wrap, {
40313                 pinned : true,
40314                 wrap: true,
40315                 dynamic : true,
40316                 minHeight : this.height,
40317                 height: this.height,
40318                 handles : this.resizable,
40319                 width: this.width,
40320                 listeners : {
40321                     resize : function(r, w, h) {
40322                         _t.onResize(w,h); // -something
40323                     }
40324                 }
40325             });
40326             
40327         }
40328
40329         this.frameId = Roo.id();
40330         
40331         this.createToolbar(this);
40332         
40333       
40334         
40335         var iframe = this.wrap.createChild({
40336             tag: 'iframe',
40337             id: this.frameId,
40338             name: this.frameId,
40339             frameBorder : 'no',
40340             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
40341         }, this.el
40342         );
40343         
40344        // console.log(iframe);
40345         //this.wrap.dom.appendChild(iframe);
40346
40347         this.iframe = iframe.dom;
40348
40349          this.assignDocWin();
40350         
40351         this.doc.designMode = 'on';
40352        
40353         this.doc.open();
40354         this.doc.write(this.getDocMarkup());
40355         this.doc.close();
40356
40357         
40358         var task = { // must defer to wait for browser to be ready
40359             run : function(){
40360                 //console.log("run task?" + this.doc.readyState);
40361                 this.assignDocWin();
40362                 if(this.doc.body || this.doc.readyState == 'complete'){
40363                     try {
40364                         this.doc.designMode="on";
40365                     } catch (e) {
40366                         return;
40367                     }
40368                     Roo.TaskMgr.stop(task);
40369                     this.initEditor.defer(10, this);
40370                 }
40371             },
40372             interval : 10,
40373             duration:10000,
40374             scope: this
40375         };
40376         Roo.TaskMgr.start(task);
40377
40378         if(!this.width){
40379             this.setSize(this.wrap.getSize());
40380         }
40381         if (this.resizeEl) {
40382             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
40383             // should trigger onReize..
40384         }
40385     },
40386
40387     // private
40388     onResize : function(w, h)
40389     {
40390         //Roo.log('resize: ' +w + ',' + h );
40391         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
40392         if(this.el && this.iframe){
40393             if(typeof w == 'number'){
40394                 var aw = w - this.wrap.getFrameWidth('lr');
40395                 this.el.setWidth(this.adjustWidth('textarea', aw));
40396                 this.iframe.style.width = aw + 'px';
40397             }
40398             if(typeof h == 'number'){
40399                 var tbh = 0;
40400                 for (var i =0; i < this.toolbars.length;i++) {
40401                     // fixme - ask toolbars for heights?
40402                     tbh += this.toolbars[i].tb.el.getHeight();
40403                     if (this.toolbars[i].footer) {
40404                         tbh += this.toolbars[i].footer.el.getHeight();
40405                     }
40406                 }
40407                 
40408                 
40409                 
40410                 
40411                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
40412                 ah -= 5; // knock a few pixes off for look..
40413                 this.el.setHeight(this.adjustWidth('textarea', ah));
40414                 this.iframe.style.height = ah + 'px';
40415                 if(this.doc){
40416                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
40417                 }
40418             }
40419         }
40420     },
40421
40422     /**
40423      * Toggles the editor between standard and source edit mode.
40424      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
40425      */
40426     toggleSourceEdit : function(sourceEditMode){
40427         
40428         this.sourceEditMode = sourceEditMode === true;
40429         
40430         if(this.sourceEditMode){
40431 //            Roo.log('in');
40432 //            Roo.log(this.syncValue());
40433             this.syncValue();
40434             this.iframe.className = 'x-hidden';
40435             this.el.removeClass('x-hidden');
40436             this.el.dom.removeAttribute('tabIndex');
40437             this.el.focus();
40438         }else{
40439 //            Roo.log('out')
40440 //            Roo.log(this.pushValue()); 
40441             this.pushValue();
40442             this.iframe.className = '';
40443             this.el.addClass('x-hidden');
40444             this.el.dom.setAttribute('tabIndex', -1);
40445             this.deferFocus();
40446         }
40447         this.setSize(this.wrap.getSize());
40448         this.fireEvent('editmodechange', this, this.sourceEditMode);
40449     },
40450
40451     // private used internally
40452     createLink : function(){
40453         var url = prompt(this.createLinkText, this.defaultLinkValue);
40454         if(url && url != 'http:/'+'/'){
40455             this.relayCmd('createlink', url);
40456         }
40457     },
40458
40459     // private (for BoxComponent)
40460     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40461
40462     // private (for BoxComponent)
40463     getResizeEl : function(){
40464         return this.wrap;
40465     },
40466
40467     // private (for BoxComponent)
40468     getPositionEl : function(){
40469         return this.wrap;
40470     },
40471
40472     // private
40473     initEvents : function(){
40474         this.originalValue = this.getValue();
40475     },
40476
40477     /**
40478      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40479      * @method
40480      */
40481     markInvalid : Roo.emptyFn,
40482     /**
40483      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40484      * @method
40485      */
40486     clearInvalid : Roo.emptyFn,
40487
40488     setValue : function(v){
40489         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
40490         this.pushValue();
40491     },
40492
40493     /**
40494      * Protected method that will not generally be called directly. If you need/want
40495      * custom HTML cleanup, this is the method you should override.
40496      * @param {String} html The HTML to be cleaned
40497      * return {String} The cleaned HTML
40498      */
40499     cleanHtml : function(html){
40500         html = String(html);
40501         if(html.length > 5){
40502             if(Roo.isSafari){ // strip safari nonsense
40503                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
40504             }
40505         }
40506         if(html == '&nbsp;'){
40507             html = '';
40508         }
40509         return html;
40510     },
40511
40512     /**
40513      * Protected method that will not generally be called directly. Syncs the contents
40514      * of the editor iframe with the textarea.
40515      */
40516     syncValue : function(){
40517         if(this.initialized){
40518             var bd = (this.doc.body || this.doc.documentElement);
40519             //this.cleanUpPaste(); -- this is done else where and causes havoc..
40520             var html = bd.innerHTML;
40521             if(Roo.isSafari){
40522                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
40523                 var m = bs.match(/text-align:(.*?);/i);
40524                 if(m && m[1]){
40525                     html = '<div style="'+m[0]+'">' + html + '</div>';
40526                 }
40527             }
40528             html = this.cleanHtml(html);
40529             // fix up the special chars.. normaly like back quotes in word...
40530             // however we do not want to do this with chinese..
40531             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
40532                 var cc = b.charCodeAt();
40533                 if (
40534                     (cc >= 0x4E00 && cc < 0xA000 ) ||
40535                     (cc >= 0x3400 && cc < 0x4E00 ) ||
40536                     (cc >= 0xf900 && cc < 0xfb00 )
40537                 ) {
40538                         return b;
40539                 }
40540                 return "&#"+cc+";" 
40541             });
40542             if(this.fireEvent('beforesync', this, html) !== false){
40543                 this.el.dom.value = html;
40544                 this.fireEvent('sync', this, html);
40545             }
40546         }
40547     },
40548
40549     /**
40550      * Protected method that will not generally be called directly. Pushes the value of the textarea
40551      * into the iframe editor.
40552      */
40553     pushValue : function(){
40554         if(this.initialized){
40555             var v = this.el.dom.value;
40556             
40557             if(v.length < 1){
40558                 v = '&#160;';
40559             }
40560             
40561             if(this.fireEvent('beforepush', this, v) !== false){
40562                 var d = (this.doc.body || this.doc.documentElement);
40563                 d.innerHTML = v;
40564                 this.cleanUpPaste();
40565                 this.el.dom.value = d.innerHTML;
40566                 this.fireEvent('push', this, v);
40567             }
40568         }
40569     },
40570
40571     // private
40572     deferFocus : function(){
40573         this.focus.defer(10, this);
40574     },
40575
40576     // doc'ed in Field
40577     focus : function(){
40578         if(this.win && !this.sourceEditMode){
40579             this.win.focus();
40580         }else{
40581             this.el.focus();
40582         }
40583     },
40584     
40585     assignDocWin: function()
40586     {
40587         var iframe = this.iframe;
40588         
40589          if(Roo.isIE){
40590             this.doc = iframe.contentWindow.document;
40591             this.win = iframe.contentWindow;
40592         } else {
40593             if (!Roo.get(this.frameId)) {
40594                 return;
40595             }
40596             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
40597             this.win = Roo.get(this.frameId).dom.contentWindow;
40598         }
40599     },
40600     
40601     // private
40602     initEditor : function(){
40603         //console.log("INIT EDITOR");
40604         this.assignDocWin();
40605         
40606         
40607         
40608         this.doc.designMode="on";
40609         this.doc.open();
40610         this.doc.write(this.getDocMarkup());
40611         this.doc.close();
40612         
40613         var dbody = (this.doc.body || this.doc.documentElement);
40614         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
40615         // this copies styles from the containing element into thsi one..
40616         // not sure why we need all of this..
40617         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
40618         ss['background-attachment'] = 'fixed'; // w3c
40619         dbody.bgProperties = 'fixed'; // ie
40620         Roo.DomHelper.applyStyles(dbody, ss);
40621         Roo.EventManager.on(this.doc, {
40622             //'mousedown': this.onEditorEvent,
40623             'mouseup': this.onEditorEvent,
40624             'dblclick': this.onEditorEvent,
40625             'click': this.onEditorEvent,
40626             'keyup': this.onEditorEvent,
40627             buffer:100,
40628             scope: this
40629         });
40630         if(Roo.isGecko){
40631             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
40632         }
40633         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
40634             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
40635         }
40636         this.initialized = true;
40637
40638         this.fireEvent('initialize', this);
40639         this.pushValue();
40640     },
40641
40642     // private
40643     onDestroy : function(){
40644         
40645         
40646         
40647         if(this.rendered){
40648             
40649             for (var i =0; i < this.toolbars.length;i++) {
40650                 // fixme - ask toolbars for heights?
40651                 this.toolbars[i].onDestroy();
40652             }
40653             
40654             this.wrap.dom.innerHTML = '';
40655             this.wrap.remove();
40656         }
40657     },
40658
40659     // private
40660     onFirstFocus : function(){
40661         
40662         this.assignDocWin();
40663         
40664         
40665         this.activated = true;
40666         for (var i =0; i < this.toolbars.length;i++) {
40667             this.toolbars[i].onFirstFocus();
40668         }
40669        
40670         if(Roo.isGecko){ // prevent silly gecko errors
40671             this.win.focus();
40672             var s = this.win.getSelection();
40673             if(!s.focusNode || s.focusNode.nodeType != 3){
40674                 var r = s.getRangeAt(0);
40675                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
40676                 r.collapse(true);
40677                 this.deferFocus();
40678             }
40679             try{
40680                 this.execCmd('useCSS', true);
40681                 this.execCmd('styleWithCSS', false);
40682             }catch(e){}
40683         }
40684         this.fireEvent('activate', this);
40685     },
40686
40687     // private
40688     adjustFont: function(btn){
40689         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
40690         //if(Roo.isSafari){ // safari
40691         //    adjust *= 2;
40692        // }
40693         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
40694         if(Roo.isSafari){ // safari
40695             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
40696             v =  (v < 10) ? 10 : v;
40697             v =  (v > 48) ? 48 : v;
40698             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
40699             
40700         }
40701         
40702         
40703         v = Math.max(1, v+adjust);
40704         
40705         this.execCmd('FontSize', v  );
40706     },
40707
40708     onEditorEvent : function(e){
40709         this.fireEvent('editorevent', this, e);
40710       //  this.updateToolbar();
40711         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
40712     },
40713
40714     insertTag : function(tg)
40715     {
40716         // could be a bit smarter... -> wrap the current selected tRoo..
40717         if (tg.toLowerCase() == 'span') {
40718             
40719             range = this.createRange(this.getSelection());
40720             var wrappingNode = this.doc.createElement("span");
40721             wrappingNode.appendChild(range.extractContents());
40722             range.insertNode(wrappingNode);
40723
40724             return;
40725             
40726             
40727             
40728         }
40729         this.execCmd("formatblock",   tg);
40730         
40731     },
40732     
40733     insertText : function(txt)
40734     {
40735         
40736         
40737         var range = this.createRange();
40738         range.deleteContents();
40739                //alert(Sender.getAttribute('label'));
40740                
40741         range.insertNode(this.doc.createTextNode(txt));
40742     } ,
40743     
40744     // private
40745     relayBtnCmd : function(btn){
40746         this.relayCmd(btn.cmd);
40747     },
40748
40749     /**
40750      * Executes a Midas editor command on the editor document and performs necessary focus and
40751      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
40752      * @param {String} cmd The Midas command
40753      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40754      */
40755     relayCmd : function(cmd, value){
40756         this.win.focus();
40757         this.execCmd(cmd, value);
40758         this.fireEvent('editorevent', this);
40759         //this.updateToolbar();
40760         this.deferFocus();
40761     },
40762
40763     /**
40764      * Executes a Midas editor command directly on the editor document.
40765      * For visual commands, you should use {@link #relayCmd} instead.
40766      * <b>This should only be called after the editor is initialized.</b>
40767      * @param {String} cmd The Midas command
40768      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40769      */
40770     execCmd : function(cmd, value){
40771         this.doc.execCommand(cmd, false, value === undefined ? null : value);
40772         this.syncValue();
40773     },
40774  
40775  
40776    
40777     /**
40778      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
40779      * to insert tRoo.
40780      * @param {String} text | dom node.. 
40781      */
40782     insertAtCursor : function(text)
40783     {
40784         
40785         
40786         
40787         if(!this.activated){
40788             return;
40789         }
40790         /*
40791         if(Roo.isIE){
40792             this.win.focus();
40793             var r = this.doc.selection.createRange();
40794             if(r){
40795                 r.collapse(true);
40796                 r.pasteHTML(text);
40797                 this.syncValue();
40798                 this.deferFocus();
40799             
40800             }
40801             return;
40802         }
40803         */
40804         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
40805             this.win.focus();
40806             
40807             
40808             // from jquery ui (MIT licenced)
40809             var range, node;
40810             var win = this.win;
40811             
40812             if (win.getSelection && win.getSelection().getRangeAt) {
40813                 range = win.getSelection().getRangeAt(0);
40814                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
40815                 range.insertNode(node);
40816             } else if (win.document.selection && win.document.selection.createRange) {
40817                 // no firefox support
40818                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40819                 win.document.selection.createRange().pasteHTML(txt);
40820             } else {
40821                 // no firefox support
40822                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40823                 this.execCmd('InsertHTML', txt);
40824             } 
40825             
40826             this.syncValue();
40827             
40828             this.deferFocus();
40829         }
40830     },
40831  // private
40832     mozKeyPress : function(e){
40833         if(e.ctrlKey){
40834             var c = e.getCharCode(), cmd;
40835           
40836             if(c > 0){
40837                 c = String.fromCharCode(c).toLowerCase();
40838                 switch(c){
40839                     case 'b':
40840                         cmd = 'bold';
40841                         break;
40842                     case 'i':
40843                         cmd = 'italic';
40844                         break;
40845                     
40846                     case 'u':
40847                         cmd = 'underline';
40848                         break;
40849                     
40850                     case 'v':
40851                         this.cleanUpPaste.defer(100, this);
40852                         return;
40853                         
40854                 }
40855                 if(cmd){
40856                     this.win.focus();
40857                     this.execCmd(cmd);
40858                     this.deferFocus();
40859                     e.preventDefault();
40860                 }
40861                 
40862             }
40863         }
40864     },
40865
40866     // private
40867     fixKeys : function(){ // load time branching for fastest keydown performance
40868         if(Roo.isIE){
40869             return function(e){
40870                 var k = e.getKey(), r;
40871                 if(k == e.TAB){
40872                     e.stopEvent();
40873                     r = this.doc.selection.createRange();
40874                     if(r){
40875                         r.collapse(true);
40876                         r.pasteHTML('&#160;&#160;&#160;&#160;');
40877                         this.deferFocus();
40878                     }
40879                     return;
40880                 }
40881                 
40882                 if(k == e.ENTER){
40883                     r = this.doc.selection.createRange();
40884                     if(r){
40885                         var target = r.parentElement();
40886                         if(!target || target.tagName.toLowerCase() != 'li'){
40887                             e.stopEvent();
40888                             r.pasteHTML('<br />');
40889                             r.collapse(false);
40890                             r.select();
40891                         }
40892                     }
40893                 }
40894                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40895                     this.cleanUpPaste.defer(100, this);
40896                     return;
40897                 }
40898                 
40899                 
40900             };
40901         }else if(Roo.isOpera){
40902             return function(e){
40903                 var k = e.getKey();
40904                 if(k == e.TAB){
40905                     e.stopEvent();
40906                     this.win.focus();
40907                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
40908                     this.deferFocus();
40909                 }
40910                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40911                     this.cleanUpPaste.defer(100, this);
40912                     return;
40913                 }
40914                 
40915             };
40916         }else if(Roo.isSafari){
40917             return function(e){
40918                 var k = e.getKey();
40919                 
40920                 if(k == e.TAB){
40921                     e.stopEvent();
40922                     this.execCmd('InsertText','\t');
40923                     this.deferFocus();
40924                     return;
40925                 }
40926                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40927                     this.cleanUpPaste.defer(100, this);
40928                     return;
40929                 }
40930                 
40931              };
40932         }
40933     }(),
40934     
40935     getAllAncestors: function()
40936     {
40937         var p = this.getSelectedNode();
40938         var a = [];
40939         if (!p) {
40940             a.push(p); // push blank onto stack..
40941             p = this.getParentElement();
40942         }
40943         
40944         
40945         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
40946             a.push(p);
40947             p = p.parentNode;
40948         }
40949         a.push(this.doc.body);
40950         return a;
40951     },
40952     lastSel : false,
40953     lastSelNode : false,
40954     
40955     
40956     getSelection : function() 
40957     {
40958         this.assignDocWin();
40959         return Roo.isIE ? this.doc.selection : this.win.getSelection();
40960     },
40961     
40962     getSelectedNode: function() 
40963     {
40964         // this may only work on Gecko!!!
40965         
40966         // should we cache this!!!!
40967         
40968         
40969         
40970          
40971         var range = this.createRange(this.getSelection()).cloneRange();
40972         
40973         if (Roo.isIE) {
40974             var parent = range.parentElement();
40975             while (true) {
40976                 var testRange = range.duplicate();
40977                 testRange.moveToElementText(parent);
40978                 if (testRange.inRange(range)) {
40979                     break;
40980                 }
40981                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
40982                     break;
40983                 }
40984                 parent = parent.parentElement;
40985             }
40986             return parent;
40987         }
40988         
40989         // is ancestor a text element.
40990         var ac =  range.commonAncestorContainer;
40991         if (ac.nodeType == 3) {
40992             ac = ac.parentNode;
40993         }
40994         
40995         var ar = ac.childNodes;
40996          
40997         var nodes = [];
40998         var other_nodes = [];
40999         var has_other_nodes = false;
41000         for (var i=0;i<ar.length;i++) {
41001             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41002                 continue;
41003             }
41004             // fullly contained node.
41005             
41006             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41007                 nodes.push(ar[i]);
41008                 continue;
41009             }
41010             
41011             // probably selected..
41012             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41013                 other_nodes.push(ar[i]);
41014                 continue;
41015             }
41016             // outer..
41017             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41018                 continue;
41019             }
41020             
41021             
41022             has_other_nodes = true;
41023         }
41024         if (!nodes.length && other_nodes.length) {
41025             nodes= other_nodes;
41026         }
41027         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41028             return false;
41029         }
41030         
41031         return nodes[0];
41032     },
41033     createRange: function(sel)
41034     {
41035         // this has strange effects when using with 
41036         // top toolbar - not sure if it's a great idea.
41037         //this.editor.contentWindow.focus();
41038         if (typeof sel != "undefined") {
41039             try {
41040                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41041             } catch(e) {
41042                 return this.doc.createRange();
41043             }
41044         } else {
41045             return this.doc.createRange();
41046         }
41047     },
41048     getParentElement: function()
41049     {
41050         
41051         this.assignDocWin();
41052         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41053         
41054         var range = this.createRange(sel);
41055          
41056         try {
41057             var p = range.commonAncestorContainer;
41058             while (p.nodeType == 3) { // text node
41059                 p = p.parentNode;
41060             }
41061             return p;
41062         } catch (e) {
41063             return null;
41064         }
41065     
41066     },
41067     /***
41068      *
41069      * Range intersection.. the hard stuff...
41070      *  '-1' = before
41071      *  '0' = hits..
41072      *  '1' = after.
41073      *         [ -- selected range --- ]
41074      *   [fail]                        [fail]
41075      *
41076      *    basically..
41077      *      if end is before start or  hits it. fail.
41078      *      if start is after end or hits it fail.
41079      *
41080      *   if either hits (but other is outside. - then it's not 
41081      *   
41082      *    
41083      **/
41084     
41085     
41086     // @see http://www.thismuchiknow.co.uk/?p=64.
41087     rangeIntersectsNode : function(range, node)
41088     {
41089         var nodeRange = node.ownerDocument.createRange();
41090         try {
41091             nodeRange.selectNode(node);
41092         } catch (e) {
41093             nodeRange.selectNodeContents(node);
41094         }
41095     
41096         var rangeStartRange = range.cloneRange();
41097         rangeStartRange.collapse(true);
41098     
41099         var rangeEndRange = range.cloneRange();
41100         rangeEndRange.collapse(false);
41101     
41102         var nodeStartRange = nodeRange.cloneRange();
41103         nodeStartRange.collapse(true);
41104     
41105         var nodeEndRange = nodeRange.cloneRange();
41106         nodeEndRange.collapse(false);
41107     
41108         return rangeStartRange.compareBoundaryPoints(
41109                  Range.START_TO_START, nodeEndRange) == -1 &&
41110                rangeEndRange.compareBoundaryPoints(
41111                  Range.START_TO_START, nodeStartRange) == 1;
41112         
41113          
41114     },
41115     rangeCompareNode : function(range, node)
41116     {
41117         var nodeRange = node.ownerDocument.createRange();
41118         try {
41119             nodeRange.selectNode(node);
41120         } catch (e) {
41121             nodeRange.selectNodeContents(node);
41122         }
41123         
41124         
41125         range.collapse(true);
41126     
41127         nodeRange.collapse(true);
41128      
41129         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41130         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41131          
41132         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41133         
41134         var nodeIsBefore   =  ss == 1;
41135         var nodeIsAfter    = ee == -1;
41136         
41137         if (nodeIsBefore && nodeIsAfter)
41138             return 0; // outer
41139         if (!nodeIsBefore && nodeIsAfter)
41140             return 1; //right trailed.
41141         
41142         if (nodeIsBefore && !nodeIsAfter)
41143             return 2;  // left trailed.
41144         // fully contined.
41145         return 3;
41146     },
41147
41148     // private? - in a new class?
41149     cleanUpPaste :  function()
41150     {
41151         // cleans up the whole document..
41152          Roo.log('cleanuppaste');
41153         this.cleanUpChildren(this.doc.body);
41154         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41155         if (clean != this.doc.body.innerHTML) {
41156             this.doc.body.innerHTML = clean;
41157         }
41158         
41159     },
41160     
41161     cleanWordChars : function(input) {// change the chars to hex code
41162         var he = Roo.form.HtmlEditor;
41163         
41164         var output = input;
41165         Roo.each(he.swapCodes, function(sw) { 
41166             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41167             
41168             output = output.replace(swapper, sw[1]);
41169         });
41170         
41171         return output;
41172     },
41173     
41174     
41175     cleanUpChildren : function (n)
41176     {
41177         if (!n.childNodes.length) {
41178             return;
41179         }
41180         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41181            this.cleanUpChild(n.childNodes[i]);
41182         }
41183     },
41184     
41185     
41186         
41187     
41188     cleanUpChild : function (node)
41189     {
41190         var ed = this;
41191         //console.log(node);
41192         if (node.nodeName == "#text") {
41193             // clean up silly Windows -- stuff?
41194             return; 
41195         }
41196         if (node.nodeName == "#comment") {
41197             node.parentNode.removeChild(node);
41198             // clean up silly Windows -- stuff?
41199             return; 
41200         }
41201         
41202         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41203             // remove node.
41204             node.parentNode.removeChild(node);
41205             return;
41206             
41207         }
41208         
41209         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41210         
41211         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41212         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41213         
41214         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41215         //    remove_keep_children = true;
41216         //}
41217         
41218         if (remove_keep_children) {
41219             this.cleanUpChildren(node);
41220             // inserts everything just before this node...
41221             while (node.childNodes.length) {
41222                 var cn = node.childNodes[0];
41223                 node.removeChild(cn);
41224                 node.parentNode.insertBefore(cn, node);
41225             }
41226             node.parentNode.removeChild(node);
41227             return;
41228         }
41229         
41230         if (!node.attributes || !node.attributes.length) {
41231             this.cleanUpChildren(node);
41232             return;
41233         }
41234         
41235         function cleanAttr(n,v)
41236         {
41237             
41238             if (v.match(/^\./) || v.match(/^\//)) {
41239                 return;
41240             }
41241             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41242                 return;
41243             }
41244             if (v.match(/^#/)) {
41245                 return;
41246             }
41247 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41248             node.removeAttribute(n);
41249             
41250         }
41251         
41252         function cleanStyle(n,v)
41253         {
41254             if (v.match(/expression/)) { //XSS?? should we even bother..
41255                 node.removeAttribute(n);
41256                 return;
41257             }
41258             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
41259             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
41260             
41261             
41262             var parts = v.split(/;/);
41263             var clean = [];
41264             
41265             Roo.each(parts, function(p) {
41266                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41267                 if (!p.length) {
41268                     return true;
41269                 }
41270                 var l = p.split(':').shift().replace(/\s+/g,'');
41271                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41272                 
41273                 
41274                 if ( cblack.indexOf(l) > -1) {
41275 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41276                     //node.removeAttribute(n);
41277                     return true;
41278                 }
41279                 //Roo.log()
41280                 // only allow 'c whitelisted system attributes'
41281                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41282 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41283                     //node.removeAttribute(n);
41284                     return true;
41285                 }
41286                 
41287                 
41288                  
41289                 
41290                 clean.push(p);
41291                 return true;
41292             });
41293             if (clean.length) { 
41294                 node.setAttribute(n, clean.join(';'));
41295             } else {
41296                 node.removeAttribute(n);
41297             }
41298             
41299         }
41300         
41301         
41302         for (var i = node.attributes.length-1; i > -1 ; i--) {
41303             var a = node.attributes[i];
41304             //console.log(a);
41305             
41306             if (a.name.toLowerCase().substr(0,2)=='on')  {
41307                 node.removeAttribute(a.name);
41308                 continue;
41309             }
41310             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
41311                 node.removeAttribute(a.name);
41312                 continue;
41313             }
41314             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
41315                 cleanAttr(a.name,a.value); // fixme..
41316                 continue;
41317             }
41318             if (a.name == 'style') {
41319                 cleanStyle(a.name,a.value);
41320                 continue;
41321             }
41322             /// clean up MS crap..
41323             // tecnically this should be a list of valid class'es..
41324             
41325             
41326             if (a.name == 'class') {
41327                 if (a.value.match(/^Mso/)) {
41328                     node.className = '';
41329                 }
41330                 
41331                 if (a.value.match(/body/)) {
41332                     node.className = '';
41333                 }
41334                 continue;
41335             }
41336             
41337             // style cleanup!?
41338             // class cleanup?
41339             
41340         }
41341         
41342         
41343         this.cleanUpChildren(node);
41344         
41345         
41346     }
41347     
41348     
41349     // hide stuff that is not compatible
41350     /**
41351      * @event blur
41352      * @hide
41353      */
41354     /**
41355      * @event change
41356      * @hide
41357      */
41358     /**
41359      * @event focus
41360      * @hide
41361      */
41362     /**
41363      * @event specialkey
41364      * @hide
41365      */
41366     /**
41367      * @cfg {String} fieldClass @hide
41368      */
41369     /**
41370      * @cfg {String} focusClass @hide
41371      */
41372     /**
41373      * @cfg {String} autoCreate @hide
41374      */
41375     /**
41376      * @cfg {String} inputType @hide
41377      */
41378     /**
41379      * @cfg {String} invalidClass @hide
41380      */
41381     /**
41382      * @cfg {String} invalidText @hide
41383      */
41384     /**
41385      * @cfg {String} msgFx @hide
41386      */
41387     /**
41388      * @cfg {String} validateOnBlur @hide
41389      */
41390 });
41391
41392 Roo.form.HtmlEditor.white = [
41393         'area', 'br', 'img', 'input', 'hr', 'wbr',
41394         
41395        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
41396        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
41397        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
41398        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
41399        'table',   'ul',         'xmp', 
41400        
41401        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
41402       'thead',   'tr', 
41403      
41404       'dir', 'menu', 'ol', 'ul', 'dl',
41405        
41406       'embed',  'object'
41407 ];
41408
41409
41410 Roo.form.HtmlEditor.black = [
41411     //    'embed',  'object', // enable - backend responsiblity to clean thiese
41412         'applet', // 
41413         'base',   'basefont', 'bgsound', 'blink',  'body', 
41414         'frame',  'frameset', 'head',    'html',   'ilayer', 
41415         'iframe', 'layer',  'link',     'meta',    'object',   
41416         'script', 'style' ,'title',  'xml' // clean later..
41417 ];
41418 Roo.form.HtmlEditor.clean = [
41419     'script', 'style', 'title', 'xml'
41420 ];
41421 Roo.form.HtmlEditor.remove = [
41422     'font'
41423 ];
41424 // attributes..
41425
41426 Roo.form.HtmlEditor.ablack = [
41427     'on'
41428 ];
41429     
41430 Roo.form.HtmlEditor.aclean = [ 
41431     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
41432 ];
41433
41434 // protocols..
41435 Roo.form.HtmlEditor.pwhite= [
41436         'http',  'https',  'mailto'
41437 ];
41438
41439 // white listed style attributes.
41440 Roo.form.HtmlEditor.cwhite= [
41441       //  'text-align', /// default is to allow most things..
41442       
41443          
41444 //        'font-size'//??
41445 ];
41446
41447 // black listed style attributes.
41448 Roo.form.HtmlEditor.cblack= [
41449       //  'font-size' -- this can be set by the project 
41450 ];
41451
41452
41453 Roo.form.HtmlEditor.swapCodes   =[ 
41454     [    8211, "--" ], 
41455     [    8212, "--" ], 
41456     [    8216,  "'" ],  
41457     [    8217, "'" ],  
41458     [    8220, '"' ],  
41459     [    8221, '"' ],  
41460     [    8226, "*" ],  
41461     [    8230, "..." ]
41462 ]; 
41463
41464     // <script type="text/javascript">
41465 /*
41466  * Based on
41467  * Ext JS Library 1.1.1
41468  * Copyright(c) 2006-2007, Ext JS, LLC.
41469  *  
41470  
41471  */
41472
41473 /**
41474  * @class Roo.form.HtmlEditorToolbar1
41475  * Basic Toolbar
41476  * 
41477  * Usage:
41478  *
41479  new Roo.form.HtmlEditor({
41480     ....
41481     toolbars : [
41482         new Roo.form.HtmlEditorToolbar1({
41483             disable : { fonts: 1 , format: 1, ..., ... , ...],
41484             btns : [ .... ]
41485         })
41486     }
41487      
41488  * 
41489  * @cfg {Object} disable List of elements to disable..
41490  * @cfg {Array} btns List of additional buttons.
41491  * 
41492  * 
41493  * NEEDS Extra CSS? 
41494  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
41495  */
41496  
41497 Roo.form.HtmlEditor.ToolbarStandard = function(config)
41498 {
41499     
41500     Roo.apply(this, config);
41501     
41502     // default disabled, based on 'good practice'..
41503     this.disable = this.disable || {};
41504     Roo.applyIf(this.disable, {
41505         fontSize : true,
41506         colors : true,
41507         specialElements : true
41508     });
41509     
41510     
41511     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
41512     // dont call parent... till later.
41513 }
41514
41515 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
41516     
41517     tb: false,
41518     
41519     rendered: false,
41520     
41521     editor : false,
41522     /**
41523      * @cfg {Object} disable  List of toolbar elements to disable
41524          
41525      */
41526     disable : false,
41527       /**
41528      * @cfg {Array} fontFamilies An array of available font families
41529      */
41530     fontFamilies : [
41531         'Arial',
41532         'Courier New',
41533         'Tahoma',
41534         'Times New Roman',
41535         'Verdana'
41536     ],
41537     
41538     specialChars : [
41539            "&#169;",
41540           "&#174;",     
41541           "&#8482;",    
41542           "&#163;" ,    
41543          // "&#8212;",    
41544           "&#8230;",    
41545           "&#247;" ,    
41546         //  "&#225;" ,     ?? a acute?
41547            "&#8364;"    , //Euro
41548        //   "&#8220;"    ,
41549         //  "&#8221;"    ,
41550         //  "&#8226;"    ,
41551           "&#176;"  //   , // degrees
41552
41553          // "&#233;"     , // e ecute
41554          // "&#250;"     , // u ecute?
41555     ],
41556     
41557     specialElements : [
41558         {
41559             text: "Insert Table",
41560             xtype: 'MenuItem',
41561             xns : Roo.Menu,
41562             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
41563                 
41564         },
41565         {    
41566             text: "Insert Image",
41567             xtype: 'MenuItem',
41568             xns : Roo.Menu,
41569             ihtml : '<img src="about:blank"/>'
41570             
41571         }
41572         
41573          
41574     ],
41575     
41576     
41577     inputElements : [ 
41578             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
41579             "input:submit", "input:button", "select", "textarea", "label" ],
41580     formats : [
41581         ["p"] ,  
41582         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
41583         ["pre"],[ "code"], 
41584         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
41585         ['div'],['span']
41586     ],
41587      /**
41588      * @cfg {String} defaultFont default font to use.
41589      */
41590     defaultFont: 'tahoma',
41591    
41592     fontSelect : false,
41593     
41594     
41595     formatCombo : false,
41596     
41597     init : function(editor)
41598     {
41599         this.editor = editor;
41600         
41601         
41602         var fid = editor.frameId;
41603         var etb = this;
41604         function btn(id, toggle, handler){
41605             var xid = fid + '-'+ id ;
41606             return {
41607                 id : xid,
41608                 cmd : id,
41609                 cls : 'x-btn-icon x-edit-'+id,
41610                 enableToggle:toggle !== false,
41611                 scope: editor, // was editor...
41612                 handler:handler||editor.relayBtnCmd,
41613                 clickEvent:'mousedown',
41614                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
41615                 tabIndex:-1
41616             };
41617         }
41618         
41619         
41620         
41621         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
41622         this.tb = tb;
41623          // stop form submits
41624         tb.el.on('click', function(e){
41625             e.preventDefault(); // what does this do?
41626         });
41627
41628         if(!this.disable.font) { // && !Roo.isSafari){
41629             /* why no safari for fonts 
41630             editor.fontSelect = tb.el.createChild({
41631                 tag:'select',
41632                 tabIndex: -1,
41633                 cls:'x-font-select',
41634                 html: this.createFontOptions()
41635             });
41636             
41637             editor.fontSelect.on('change', function(){
41638                 var font = editor.fontSelect.dom.value;
41639                 editor.relayCmd('fontname', font);
41640                 editor.deferFocus();
41641             }, editor);
41642             
41643             tb.add(
41644                 editor.fontSelect.dom,
41645                 '-'
41646             );
41647             */
41648             
41649         };
41650         if(!this.disable.formats){
41651             this.formatCombo = new Roo.form.ComboBox({
41652                 store: new Roo.data.SimpleStore({
41653                     id : 'tag',
41654                     fields: ['tag'],
41655                     data : this.formats // from states.js
41656                 }),
41657                 blockFocus : true,
41658                 name : '',
41659                 //autoCreate : {tag: "div",  size: "20"},
41660                 displayField:'tag',
41661                 typeAhead: false,
41662                 mode: 'local',
41663                 editable : false,
41664                 triggerAction: 'all',
41665                 emptyText:'Add tag',
41666                 selectOnFocus:true,
41667                 width:135,
41668                 listeners : {
41669                     'select': function(c, r, i) {
41670                         editor.insertTag(r.get('tag'));
41671                         editor.focus();
41672                     }
41673                 }
41674
41675             });
41676             tb.addField(this.formatCombo);
41677             
41678         }
41679         
41680         if(!this.disable.format){
41681             tb.add(
41682                 btn('bold'),
41683                 btn('italic'),
41684                 btn('underline')
41685             );
41686         };
41687         if(!this.disable.fontSize){
41688             tb.add(
41689                 '-',
41690                 
41691                 
41692                 btn('increasefontsize', false, editor.adjustFont),
41693                 btn('decreasefontsize', false, editor.adjustFont)
41694             );
41695         };
41696         
41697         
41698         if(!this.disable.colors){
41699             tb.add(
41700                 '-', {
41701                     id:editor.frameId +'-forecolor',
41702                     cls:'x-btn-icon x-edit-forecolor',
41703                     clickEvent:'mousedown',
41704                     tooltip: this.buttonTips['forecolor'] || undefined,
41705                     tabIndex:-1,
41706                     menu : new Roo.menu.ColorMenu({
41707                         allowReselect: true,
41708                         focus: Roo.emptyFn,
41709                         value:'000000',
41710                         plain:true,
41711                         selectHandler: function(cp, color){
41712                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
41713                             editor.deferFocus();
41714                         },
41715                         scope: editor,
41716                         clickEvent:'mousedown'
41717                     })
41718                 }, {
41719                     id:editor.frameId +'backcolor',
41720                     cls:'x-btn-icon x-edit-backcolor',
41721                     clickEvent:'mousedown',
41722                     tooltip: this.buttonTips['backcolor'] || undefined,
41723                     tabIndex:-1,
41724                     menu : new Roo.menu.ColorMenu({
41725                         focus: Roo.emptyFn,
41726                         value:'FFFFFF',
41727                         plain:true,
41728                         allowReselect: true,
41729                         selectHandler: function(cp, color){
41730                             if(Roo.isGecko){
41731                                 editor.execCmd('useCSS', false);
41732                                 editor.execCmd('hilitecolor', color);
41733                                 editor.execCmd('useCSS', true);
41734                                 editor.deferFocus();
41735                             }else{
41736                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
41737                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
41738                                 editor.deferFocus();
41739                             }
41740                         },
41741                         scope:editor,
41742                         clickEvent:'mousedown'
41743                     })
41744                 }
41745             );
41746         };
41747         // now add all the items...
41748         
41749
41750         if(!this.disable.alignments){
41751             tb.add(
41752                 '-',
41753                 btn('justifyleft'),
41754                 btn('justifycenter'),
41755                 btn('justifyright')
41756             );
41757         };
41758
41759         //if(!Roo.isSafari){
41760             if(!this.disable.links){
41761                 tb.add(
41762                     '-',
41763                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
41764                 );
41765             };
41766
41767             if(!this.disable.lists){
41768                 tb.add(
41769                     '-',
41770                     btn('insertorderedlist'),
41771                     btn('insertunorderedlist')
41772                 );
41773             }
41774             if(!this.disable.sourceEdit){
41775                 tb.add(
41776                     '-',
41777                     btn('sourceedit', true, function(btn){
41778                         this.toggleSourceEdit(btn.pressed);
41779                     })
41780                 );
41781             }
41782         //}
41783         
41784         var smenu = { };
41785         // special menu.. - needs to be tidied up..
41786         if (!this.disable.special) {
41787             smenu = {
41788                 text: "&#169;",
41789                 cls: 'x-edit-none',
41790                 
41791                 menu : {
41792                     items : []
41793                 }
41794             };
41795             for (var i =0; i < this.specialChars.length; i++) {
41796                 smenu.menu.items.push({
41797                     
41798                     html: this.specialChars[i],
41799                     handler: function(a,b) {
41800                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
41801                         //editor.insertAtCursor(a.html);
41802                         
41803                     },
41804                     tabIndex:-1
41805                 });
41806             }
41807             
41808             
41809             tb.add(smenu);
41810             
41811             
41812         }
41813          
41814         if (!this.disable.specialElements) {
41815             var semenu = {
41816                 text: "Other;",
41817                 cls: 'x-edit-none',
41818                 menu : {
41819                     items : []
41820                 }
41821             };
41822             for (var i =0; i < this.specialElements.length; i++) {
41823                 semenu.menu.items.push(
41824                     Roo.apply({ 
41825                         handler: function(a,b) {
41826                             editor.insertAtCursor(this.ihtml);
41827                         }
41828                     }, this.specialElements[i])
41829                 );
41830                     
41831             }
41832             
41833             tb.add(semenu);
41834             
41835             
41836         }
41837          
41838         
41839         if (this.btns) {
41840             for(var i =0; i< this.btns.length;i++) {
41841                 var b = Roo.factory(this.btns[i],Roo.form);
41842                 b.cls =  'x-edit-none';
41843                 b.scope = editor;
41844                 tb.add(b);
41845             }
41846         
41847         }
41848         
41849         
41850         
41851         // disable everything...
41852         
41853         this.tb.items.each(function(item){
41854            if(item.id != editor.frameId+ '-sourceedit'){
41855                 item.disable();
41856             }
41857         });
41858         this.rendered = true;
41859         
41860         // the all the btns;
41861         editor.on('editorevent', this.updateToolbar, this);
41862         // other toolbars need to implement this..
41863         //editor.on('editmodechange', this.updateToolbar, this);
41864     },
41865     
41866     
41867     
41868     /**
41869      * Protected method that will not generally be called directly. It triggers
41870      * a toolbar update by reading the markup state of the current selection in the editor.
41871      */
41872     updateToolbar: function(){
41873
41874         if(!this.editor.activated){
41875             this.editor.onFirstFocus();
41876             return;
41877         }
41878
41879         var btns = this.tb.items.map, 
41880             doc = this.editor.doc,
41881             frameId = this.editor.frameId;
41882
41883         if(!this.disable.font && !Roo.isSafari){
41884             /*
41885             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
41886             if(name != this.fontSelect.dom.value){
41887                 this.fontSelect.dom.value = name;
41888             }
41889             */
41890         }
41891         if(!this.disable.format){
41892             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
41893             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
41894             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
41895         }
41896         if(!this.disable.alignments){
41897             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
41898             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
41899             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
41900         }
41901         if(!Roo.isSafari && !this.disable.lists){
41902             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
41903             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
41904         }
41905         
41906         var ans = this.editor.getAllAncestors();
41907         if (this.formatCombo) {
41908             
41909             
41910             var store = this.formatCombo.store;
41911             this.formatCombo.setValue("");
41912             for (var i =0; i < ans.length;i++) {
41913                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
41914                     // select it..
41915                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
41916                     break;
41917                 }
41918             }
41919         }
41920         
41921         
41922         
41923         // hides menus... - so this cant be on a menu...
41924         Roo.menu.MenuMgr.hideAll();
41925
41926         //this.editorsyncValue();
41927     },
41928    
41929     
41930     createFontOptions : function(){
41931         var buf = [], fs = this.fontFamilies, ff, lc;
41932         
41933         
41934         
41935         for(var i = 0, len = fs.length; i< len; i++){
41936             ff = fs[i];
41937             lc = ff.toLowerCase();
41938             buf.push(
41939                 '<option value="',lc,'" style="font-family:',ff,';"',
41940                     (this.defaultFont == lc ? ' selected="true">' : '>'),
41941                     ff,
41942                 '</option>'
41943             );
41944         }
41945         return buf.join('');
41946     },
41947     
41948     toggleSourceEdit : function(sourceEditMode){
41949         if(sourceEditMode === undefined){
41950             sourceEditMode = !this.sourceEditMode;
41951         }
41952         this.sourceEditMode = sourceEditMode === true;
41953         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
41954         // just toggle the button?
41955         if(btn.pressed !== this.editor.sourceEditMode){
41956             btn.toggle(this.editor.sourceEditMode);
41957             return;
41958         }
41959         
41960         if(this.sourceEditMode){
41961             this.tb.items.each(function(item){
41962                 if(item.cmd != 'sourceedit'){
41963                     item.disable();
41964                 }
41965             });
41966           
41967         }else{
41968             if(this.initialized){
41969                 this.tb.items.each(function(item){
41970                     item.enable();
41971                 });
41972             }
41973             
41974         }
41975         // tell the editor that it's been pressed..
41976         this.editor.toggleSourceEdit(sourceEditMode);
41977        
41978     },
41979      /**
41980      * Object collection of toolbar tooltips for the buttons in the editor. The key
41981      * is the command id associated with that button and the value is a valid QuickTips object.
41982      * For example:
41983 <pre><code>
41984 {
41985     bold : {
41986         title: 'Bold (Ctrl+B)',
41987         text: 'Make the selected text bold.',
41988         cls: 'x-html-editor-tip'
41989     },
41990     italic : {
41991         title: 'Italic (Ctrl+I)',
41992         text: 'Make the selected text italic.',
41993         cls: 'x-html-editor-tip'
41994     },
41995     ...
41996 </code></pre>
41997     * @type Object
41998      */
41999     buttonTips : {
42000         bold : {
42001             title: 'Bold (Ctrl+B)',
42002             text: 'Make the selected text bold.',
42003             cls: 'x-html-editor-tip'
42004         },
42005         italic : {
42006             title: 'Italic (Ctrl+I)',
42007             text: 'Make the selected text italic.',
42008             cls: 'x-html-editor-tip'
42009         },
42010         underline : {
42011             title: 'Underline (Ctrl+U)',
42012             text: 'Underline the selected text.',
42013             cls: 'x-html-editor-tip'
42014         },
42015         increasefontsize : {
42016             title: 'Grow Text',
42017             text: 'Increase the font size.',
42018             cls: 'x-html-editor-tip'
42019         },
42020         decreasefontsize : {
42021             title: 'Shrink Text',
42022             text: 'Decrease the font size.',
42023             cls: 'x-html-editor-tip'
42024         },
42025         backcolor : {
42026             title: 'Text Highlight Color',
42027             text: 'Change the background color of the selected text.',
42028             cls: 'x-html-editor-tip'
42029         },
42030         forecolor : {
42031             title: 'Font Color',
42032             text: 'Change the color of the selected text.',
42033             cls: 'x-html-editor-tip'
42034         },
42035         justifyleft : {
42036             title: 'Align Text Left',
42037             text: 'Align text to the left.',
42038             cls: 'x-html-editor-tip'
42039         },
42040         justifycenter : {
42041             title: 'Center Text',
42042             text: 'Center text in the editor.',
42043             cls: 'x-html-editor-tip'
42044         },
42045         justifyright : {
42046             title: 'Align Text Right',
42047             text: 'Align text to the right.',
42048             cls: 'x-html-editor-tip'
42049         },
42050         insertunorderedlist : {
42051             title: 'Bullet List',
42052             text: 'Start a bulleted list.',
42053             cls: 'x-html-editor-tip'
42054         },
42055         insertorderedlist : {
42056             title: 'Numbered List',
42057             text: 'Start a numbered list.',
42058             cls: 'x-html-editor-tip'
42059         },
42060         createlink : {
42061             title: 'Hyperlink',
42062             text: 'Make the selected text a hyperlink.',
42063             cls: 'x-html-editor-tip'
42064         },
42065         sourceedit : {
42066             title: 'Source Edit',
42067             text: 'Switch to source editing mode.',
42068             cls: 'x-html-editor-tip'
42069         }
42070     },
42071     // private
42072     onDestroy : function(){
42073         if(this.rendered){
42074             
42075             this.tb.items.each(function(item){
42076                 if(item.menu){
42077                     item.menu.removeAll();
42078                     if(item.menu.el){
42079                         item.menu.el.destroy();
42080                     }
42081                 }
42082                 item.destroy();
42083             });
42084              
42085         }
42086     },
42087     onFirstFocus: function() {
42088         this.tb.items.each(function(item){
42089            item.enable();
42090         });
42091     }
42092 });
42093
42094
42095
42096
42097 // <script type="text/javascript">
42098 /*
42099  * Based on
42100  * Ext JS Library 1.1.1
42101  * Copyright(c) 2006-2007, Ext JS, LLC.
42102  *  
42103  
42104  */
42105
42106  
42107 /**
42108  * @class Roo.form.HtmlEditor.ToolbarContext
42109  * Context Toolbar
42110  * 
42111  * Usage:
42112  *
42113  new Roo.form.HtmlEditor({
42114     ....
42115     toolbars : [
42116         { xtype: 'ToolbarStandard', styles : {} }
42117         { xtype: 'ToolbarContext', disable : {} }
42118     ]
42119 })
42120
42121      
42122  * 
42123  * @config : {Object} disable List of elements to disable.. (not done yet.)
42124  * @config : {Object} styles  Map of styles available.
42125  * 
42126  */
42127
42128 Roo.form.HtmlEditor.ToolbarContext = function(config)
42129 {
42130     
42131     Roo.apply(this, config);
42132     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42133     // dont call parent... till later.
42134     this.styles = this.styles || {};
42135 }
42136
42137  
42138
42139 Roo.form.HtmlEditor.ToolbarContext.types = {
42140     'IMG' : {
42141         width : {
42142             title: "Width",
42143             width: 40
42144         },
42145         height:  {
42146             title: "Height",
42147             width: 40
42148         },
42149         align: {
42150             title: "Align",
42151             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42152             width : 80
42153             
42154         },
42155         border: {
42156             title: "Border",
42157             width: 40
42158         },
42159         alt: {
42160             title: "Alt",
42161             width: 120
42162         },
42163         src : {
42164             title: "Src",
42165             width: 220
42166         }
42167         
42168     },
42169     'A' : {
42170         name : {
42171             title: "Name",
42172             width: 50
42173         },
42174         href:  {
42175             title: "Href",
42176             width: 220
42177         } // border?
42178         
42179     },
42180     'TABLE' : {
42181         rows : {
42182             title: "Rows",
42183             width: 20
42184         },
42185         cols : {
42186             title: "Cols",
42187             width: 20
42188         },
42189         width : {
42190             title: "Width",
42191             width: 40
42192         },
42193         height : {
42194             title: "Height",
42195             width: 40
42196         },
42197         border : {
42198             title: "Border",
42199             width: 20
42200         }
42201     },
42202     'TD' : {
42203         width : {
42204             title: "Width",
42205             width: 40
42206         },
42207         height : {
42208             title: "Height",
42209             width: 40
42210         },   
42211         align: {
42212             title: "Align",
42213             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42214             width: 80
42215         },
42216         valign: {
42217             title: "Valign",
42218             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
42219             width: 80
42220         },
42221         colspan: {
42222             title: "Colspan",
42223             width: 20
42224             
42225         },
42226          'font-family'  : {
42227             title : "Font",
42228             style : 'fontFamily',
42229             displayField: 'display',
42230             optname : 'font-family',
42231             width: 140
42232         }
42233     },
42234     'INPUT' : {
42235         name : {
42236             title: "name",
42237             width: 120
42238         },
42239         value : {
42240             title: "Value",
42241             width: 120
42242         },
42243         width : {
42244             title: "Width",
42245             width: 40
42246         }
42247     },
42248     'LABEL' : {
42249         'for' : {
42250             title: "For",
42251             width: 120
42252         }
42253     },
42254     'TEXTAREA' : {
42255           name : {
42256             title: "name",
42257             width: 120
42258         },
42259         rows : {
42260             title: "Rows",
42261             width: 20
42262         },
42263         cols : {
42264             title: "Cols",
42265             width: 20
42266         }
42267     },
42268     'SELECT' : {
42269         name : {
42270             title: "name",
42271             width: 120
42272         },
42273         selectoptions : {
42274             title: "Options",
42275             width: 200
42276         }
42277     },
42278     
42279     // should we really allow this??
42280     // should this just be 
42281     'BODY' : {
42282         title : {
42283             title: "Title",
42284             width: 200,
42285             disabled : true
42286         }
42287     },
42288     'SPAN' : {
42289         'font-family'  : {
42290             title : "Font",
42291             style : 'fontFamily',
42292             displayField: 'display',
42293             optname : 'font-family',
42294             width: 140
42295         }
42296     },
42297     'DIV' : {
42298         'font-family'  : {
42299             title : "Font",
42300             style : 'fontFamily',
42301             displayField: 'display',
42302             optname : 'font-family',
42303             width: 140
42304         }
42305     },
42306      'P' : {
42307         'font-family'  : {
42308             title : "Font",
42309             style : 'fontFamily',
42310             displayField: 'display',
42311             optname : 'font-family',
42312             width: 140
42313         }
42314     },
42315     
42316     '*' : {
42317         // empty..
42318     }
42319
42320 };
42321
42322 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
42323 Roo.form.HtmlEditor.ToolbarContext.stores = false;
42324
42325 Roo.form.HtmlEditor.ToolbarContext.options = {
42326         'font-family'  : [ 
42327                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
42328                 [ 'Courier New', 'Courier New'],
42329                 [ 'Tahoma', 'Tahoma'],
42330                 [ 'Times New Roman,serif', 'Times'],
42331                 [ 'Verdana','Verdana' ]
42332         ]
42333 };
42334
42335 // fixme - these need to be configurable..
42336  
42337
42338 Roo.form.HtmlEditor.ToolbarContext.types
42339
42340
42341 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
42342     
42343     tb: false,
42344     
42345     rendered: false,
42346     
42347     editor : false,
42348     /**
42349      * @cfg {Object} disable  List of toolbar elements to disable
42350          
42351      */
42352     disable : false,
42353     /**
42354      * @cfg {Object} styles List of styles 
42355      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
42356      *
42357      * These must be defined in the page, so they get rendered correctly..
42358      * .headline { }
42359      * TD.underline { }
42360      * 
42361      */
42362     styles : false,
42363     
42364     options: false,
42365     
42366     toolbars : false,
42367     
42368     init : function(editor)
42369     {
42370         this.editor = editor;
42371         
42372         
42373         var fid = editor.frameId;
42374         var etb = this;
42375         function btn(id, toggle, handler){
42376             var xid = fid + '-'+ id ;
42377             return {
42378                 id : xid,
42379                 cmd : id,
42380                 cls : 'x-btn-icon x-edit-'+id,
42381                 enableToggle:toggle !== false,
42382                 scope: editor, // was editor...
42383                 handler:handler||editor.relayBtnCmd,
42384                 clickEvent:'mousedown',
42385                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42386                 tabIndex:-1
42387             };
42388         }
42389         // create a new element.
42390         var wdiv = editor.wrap.createChild({
42391                 tag: 'div'
42392             }, editor.wrap.dom.firstChild.nextSibling, true);
42393         
42394         // can we do this more than once??
42395         
42396          // stop form submits
42397       
42398  
42399         // disable everything...
42400         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42401         this.toolbars = {};
42402            
42403         for (var i in  ty) {
42404           
42405             this.toolbars[i] = this.buildToolbar(ty[i],i);
42406         }
42407         this.tb = this.toolbars.BODY;
42408         this.tb.el.show();
42409         this.buildFooter();
42410         this.footer.show();
42411         editor.on('hide', function( ) { this.footer.hide() }, this);
42412         editor.on('show', function( ) { this.footer.show() }, this);
42413         
42414          
42415         this.rendered = true;
42416         
42417         // the all the btns;
42418         editor.on('editorevent', this.updateToolbar, this);
42419         // other toolbars need to implement this..
42420         //editor.on('editmodechange', this.updateToolbar, this);
42421     },
42422     
42423     
42424     
42425     /**
42426      * Protected method that will not generally be called directly. It triggers
42427      * a toolbar update by reading the markup state of the current selection in the editor.
42428      */
42429     updateToolbar: function(editor,ev,sel){
42430
42431         //Roo.log(ev);
42432         // capture mouse up - this is handy for selecting images..
42433         // perhaps should go somewhere else...
42434         if(!this.editor.activated){
42435              this.editor.onFirstFocus();
42436             return;
42437         }
42438         
42439         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
42440         // selectNode - might want to handle IE?
42441         if (ev &&
42442             (ev.type == 'mouseup' || ev.type == 'click' ) &&
42443             ev.target && ev.target.tagName == 'IMG') {
42444             // they have click on an image...
42445             // let's see if we can change the selection...
42446             sel = ev.target;
42447          
42448               var nodeRange = sel.ownerDocument.createRange();
42449             try {
42450                 nodeRange.selectNode(sel);
42451             } catch (e) {
42452                 nodeRange.selectNodeContents(sel);
42453             }
42454             //nodeRange.collapse(true);
42455             var s = editor.win.getSelection();
42456             s.removeAllRanges();
42457             s.addRange(nodeRange);
42458         }  
42459         
42460       
42461         var updateFooter = sel ? false : true;
42462         
42463         
42464         var ans = this.editor.getAllAncestors();
42465         
42466         // pick
42467         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42468         
42469         if (!sel) { 
42470             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
42471             sel = sel ? sel : this.editor.doc.body;
42472             sel = sel.tagName.length ? sel : this.editor.doc.body;
42473             
42474         }
42475         // pick a menu that exists..
42476         var tn = sel.tagName.toUpperCase();
42477         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
42478         
42479         tn = sel.tagName.toUpperCase();
42480         
42481         var lastSel = this.tb.selectedNode
42482         
42483         this.tb.selectedNode = sel;
42484         
42485         // if current menu does not match..
42486         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
42487                 
42488             this.tb.el.hide();
42489             ///console.log("show: " + tn);
42490             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
42491             this.tb.el.show();
42492             // update name
42493             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
42494             
42495             
42496             // update attributes
42497             if (this.tb.fields) {
42498                 this.tb.fields.each(function(e) {
42499                     if (e.stylename) {
42500                         e.setValue(sel.style[e.stylename]);
42501                         return;
42502                     } 
42503                    e.setValue(sel.getAttribute(e.attrname));
42504                 });
42505             }
42506             
42507             var hasStyles = false;
42508             for(var i in this.styles) {
42509                 hasStyles = true;
42510                 break;
42511             }
42512             
42513             // update styles
42514             if (hasStyles) { 
42515                 var st = this.tb.fields.item(0);
42516                 
42517                 st.store.removeAll();
42518                
42519                 
42520                 var cn = sel.className.split(/\s+/);
42521                 
42522                 var avs = [];
42523                 if (this.styles['*']) {
42524                     
42525                     Roo.each(this.styles['*'], function(v) {
42526                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42527                     });
42528                 }
42529                 if (this.styles[tn]) { 
42530                     Roo.each(this.styles[tn], function(v) {
42531                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42532                     });
42533                 }
42534                 
42535                 st.store.loadData(avs);
42536                 st.collapse();
42537                 st.setValue(cn);
42538             }
42539             // flag our selected Node.
42540             this.tb.selectedNode = sel;
42541            
42542            
42543             Roo.menu.MenuMgr.hideAll();
42544
42545         }
42546         
42547         if (!updateFooter) {
42548             //this.footDisp.dom.innerHTML = ''; 
42549             return;
42550         }
42551         // update the footer
42552         //
42553         var html = '';
42554         
42555         this.footerEls = ans.reverse();
42556         Roo.each(this.footerEls, function(a,i) {
42557             if (!a) { return; }
42558             html += html.length ? ' &gt; '  :  '';
42559             
42560             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
42561             
42562         });
42563        
42564         // 
42565         var sz = this.footDisp.up('td').getSize();
42566         this.footDisp.dom.style.width = (sz.width -10) + 'px';
42567         this.footDisp.dom.style.marginLeft = '5px';
42568         
42569         this.footDisp.dom.style.overflow = 'hidden';
42570         
42571         this.footDisp.dom.innerHTML = html;
42572             
42573         //this.editorsyncValue();
42574     },
42575      
42576     
42577    
42578        
42579     // private
42580     onDestroy : function(){
42581         if(this.rendered){
42582             
42583             this.tb.items.each(function(item){
42584                 if(item.menu){
42585                     item.menu.removeAll();
42586                     if(item.menu.el){
42587                         item.menu.el.destroy();
42588                     }
42589                 }
42590                 item.destroy();
42591             });
42592              
42593         }
42594     },
42595     onFirstFocus: function() {
42596         // need to do this for all the toolbars..
42597         this.tb.items.each(function(item){
42598            item.enable();
42599         });
42600     },
42601     buildToolbar: function(tlist, nm)
42602     {
42603         var editor = this.editor;
42604          // create a new element.
42605         var wdiv = editor.wrap.createChild({
42606                 tag: 'div'
42607             }, editor.wrap.dom.firstChild.nextSibling, true);
42608         
42609        
42610         var tb = new Roo.Toolbar(wdiv);
42611         // add the name..
42612         
42613         tb.add(nm+ ":&nbsp;");
42614         
42615         var styles = [];
42616         for(var i in this.styles) {
42617             styles.push(i);
42618         }
42619         
42620         // styles...
42621         if (styles && styles.length) {
42622             
42623             // this needs a multi-select checkbox...
42624             tb.addField( new Roo.form.ComboBox({
42625                 store: new Roo.data.SimpleStore({
42626                     id : 'val',
42627                     fields: ['val', 'selected'],
42628                     data : [] 
42629                 }),
42630                 name : '-roo-edit-className',
42631                 attrname : 'className',
42632                 displayField: 'val',
42633                 typeAhead: false,
42634                 mode: 'local',
42635                 editable : false,
42636                 triggerAction: 'all',
42637                 emptyText:'Select Style',
42638                 selectOnFocus:true,
42639                 width: 130,
42640                 listeners : {
42641                     'select': function(c, r, i) {
42642                         // initial support only for on class per el..
42643                         tb.selectedNode.className =  r ? r.get('val') : '';
42644                         editor.syncValue();
42645                     }
42646                 }
42647     
42648             }));
42649         }
42650         
42651         var tbc = Roo.form.HtmlEditor.ToolbarContext;
42652         var tbops = tbc.options;
42653         
42654         for (var i in tlist) {
42655             
42656             var item = tlist[i];
42657             tb.add(item.title + ":&nbsp;");
42658             
42659             
42660             //optname == used so you can configure the options available..
42661             var opts = item.opts ? item.opts : false;
42662             if (item.optname) {
42663                 opts = tbops[item.optname];
42664            
42665             }
42666             
42667             if (opts) {
42668                 // opts == pulldown..
42669                 tb.addField( new Roo.form.ComboBox({
42670                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
42671                         id : 'val',
42672                         fields: ['val', 'display'],
42673                         data : opts  
42674                     }),
42675                     name : '-roo-edit-' + i,
42676                     attrname : i,
42677                     stylename : item.style ? item.style : false,
42678                     displayField: item.displayField ? item.displayField : 'val',
42679                     valueField :  'val',
42680                     typeAhead: false,
42681                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
42682                     editable : false,
42683                     triggerAction: 'all',
42684                     emptyText:'Select',
42685                     selectOnFocus:true,
42686                     width: item.width ? item.width  : 130,
42687                     listeners : {
42688                         'select': function(c, r, i) {
42689                             if (c.stylename) {
42690                                 tb.selectedNode.style[c.stylename] =  r.get('val');
42691                                 return;
42692                             }
42693                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
42694                         }
42695                     }
42696
42697                 }));
42698                 continue;
42699                     
42700                  
42701                 
42702                 tb.addField( new Roo.form.TextField({
42703                     name: i,
42704                     width: 100,
42705                     //allowBlank:false,
42706                     value: ''
42707                 }));
42708                 continue;
42709             }
42710             tb.addField( new Roo.form.TextField({
42711                 name: '-roo-edit-' + i,
42712                 attrname : i,
42713                 
42714                 width: item.width,
42715                 //allowBlank:true,
42716                 value: '',
42717                 listeners: {
42718                     'change' : function(f, nv, ov) {
42719                         tb.selectedNode.setAttribute(f.attrname, nv);
42720                     }
42721                 }
42722             }));
42723              
42724         }
42725         tb.addFill();
42726         var _this = this;
42727         tb.addButton( {
42728             text: 'Remove Tag',
42729     
42730             listeners : {
42731                 click : function ()
42732                 {
42733                     // remove
42734                     // undo does not work.
42735                      
42736                     var sn = tb.selectedNode;
42737                     
42738                     var pn = sn.parentNode;
42739                     
42740                     var stn =  sn.childNodes[0];
42741                     var en = sn.childNodes[sn.childNodes.length - 1 ];
42742                     while (sn.childNodes.length) {
42743                         var node = sn.childNodes[0];
42744                         sn.removeChild(node);
42745                         //Roo.log(node);
42746                         pn.insertBefore(node, sn);
42747                         
42748                     }
42749                     pn.removeChild(sn);
42750                     var range = editor.createRange();
42751         
42752                     range.setStart(stn,0);
42753                     range.setEnd(en,0); //????
42754                     //range.selectNode(sel);
42755                     
42756                     
42757                     var selection = editor.getSelection();
42758                     selection.removeAllRanges();
42759                     selection.addRange(range);
42760                     
42761                     
42762                     
42763                     //_this.updateToolbar(null, null, pn);
42764                     _this.updateToolbar(null, null, null);
42765                     _this.footDisp.dom.innerHTML = ''; 
42766                 }
42767             }
42768             
42769                     
42770                 
42771             
42772         });
42773         
42774         
42775         tb.el.on('click', function(e){
42776             e.preventDefault(); // what does this do?
42777         });
42778         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
42779         tb.el.hide();
42780         tb.name = nm;
42781         // dont need to disable them... as they will get hidden
42782         return tb;
42783          
42784         
42785     },
42786     buildFooter : function()
42787     {
42788         
42789         var fel = this.editor.wrap.createChild();
42790         this.footer = new Roo.Toolbar(fel);
42791         // toolbar has scrolly on left / right?
42792         var footDisp= new Roo.Toolbar.Fill();
42793         var _t = this;
42794         this.footer.add(
42795             {
42796                 text : '&lt;',
42797                 xtype: 'Button',
42798                 handler : function() {
42799                     _t.footDisp.scrollTo('left',0,true)
42800                 }
42801             }
42802         );
42803         this.footer.add( footDisp );
42804         this.footer.add( 
42805             {
42806                 text : '&gt;',
42807                 xtype: 'Button',
42808                 handler : function() {
42809                     // no animation..
42810                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
42811                 }
42812             }
42813         );
42814         var fel = Roo.get(footDisp.el);
42815         fel.addClass('x-editor-context');
42816         this.footDispWrap = fel; 
42817         this.footDispWrap.overflow  = 'hidden';
42818         
42819         this.footDisp = fel.createChild();
42820         this.footDispWrap.on('click', this.onContextClick, this)
42821         
42822         
42823     },
42824     onContextClick : function (ev,dom)
42825     {
42826         ev.preventDefault();
42827         var  cn = dom.className;
42828         //Roo.log(cn);
42829         if (!cn.match(/x-ed-loc-/)) {
42830             return;
42831         }
42832         var n = cn.split('-').pop();
42833         var ans = this.footerEls;
42834         var sel = ans[n];
42835         
42836          // pick
42837         var range = this.editor.createRange();
42838         
42839         range.selectNodeContents(sel);
42840         //range.selectNode(sel);
42841         
42842         
42843         var selection = this.editor.getSelection();
42844         selection.removeAllRanges();
42845         selection.addRange(range);
42846         
42847         
42848         
42849         this.updateToolbar(null, null, sel);
42850         
42851         
42852     }
42853     
42854     
42855     
42856     
42857     
42858 });
42859
42860
42861
42862
42863
42864 /*
42865  * Based on:
42866  * Ext JS Library 1.1.1
42867  * Copyright(c) 2006-2007, Ext JS, LLC.
42868  *
42869  * Originally Released Under LGPL - original licence link has changed is not relivant.
42870  *
42871  * Fork - LGPL
42872  * <script type="text/javascript">
42873  */
42874  
42875 /**
42876  * @class Roo.form.BasicForm
42877  * @extends Roo.util.Observable
42878  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
42879  * @constructor
42880  * @param {String/HTMLElement/Roo.Element} el The form element or its id
42881  * @param {Object} config Configuration options
42882  */
42883 Roo.form.BasicForm = function(el, config){
42884     this.allItems = [];
42885     this.childForms = [];
42886     Roo.apply(this, config);
42887     /*
42888      * The Roo.form.Field items in this form.
42889      * @type MixedCollection
42890      */
42891      
42892      
42893     this.items = new Roo.util.MixedCollection(false, function(o){
42894         return o.id || (o.id = Roo.id());
42895     });
42896     this.addEvents({
42897         /**
42898          * @event beforeaction
42899          * Fires before any action is performed. Return false to cancel the action.
42900          * @param {Form} this
42901          * @param {Action} action The action to be performed
42902          */
42903         beforeaction: true,
42904         /**
42905          * @event actionfailed
42906          * Fires when an action fails.
42907          * @param {Form} this
42908          * @param {Action} action The action that failed
42909          */
42910         actionfailed : true,
42911         /**
42912          * @event actioncomplete
42913          * Fires when an action is completed.
42914          * @param {Form} this
42915          * @param {Action} action The action that completed
42916          */
42917         actioncomplete : true
42918     });
42919     if(el){
42920         this.initEl(el);
42921     }
42922     Roo.form.BasicForm.superclass.constructor.call(this);
42923 };
42924
42925 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
42926     /**
42927      * @cfg {String} method
42928      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
42929      */
42930     /**
42931      * @cfg {DataReader} reader
42932      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
42933      * This is optional as there is built-in support for processing JSON.
42934      */
42935     /**
42936      * @cfg {DataReader} errorReader
42937      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
42938      * This is completely optional as there is built-in support for processing JSON.
42939      */
42940     /**
42941      * @cfg {String} url
42942      * The URL to use for form actions if one isn't supplied in the action options.
42943      */
42944     /**
42945      * @cfg {Boolean} fileUpload
42946      * Set to true if this form is a file upload.
42947      */
42948      
42949     /**
42950      * @cfg {Object} baseParams
42951      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
42952      */
42953      /**
42954      
42955     /**
42956      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
42957      */
42958     timeout: 30,
42959
42960     // private
42961     activeAction : null,
42962
42963     /**
42964      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
42965      * or setValues() data instead of when the form was first created.
42966      */
42967     trackResetOnLoad : false,
42968     
42969     
42970     /**
42971      * childForms - used for multi-tab forms
42972      * @type {Array}
42973      */
42974     childForms : false,
42975     
42976     /**
42977      * allItems - full list of fields.
42978      * @type {Array}
42979      */
42980     allItems : false,
42981     
42982     /**
42983      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
42984      * element by passing it or its id or mask the form itself by passing in true.
42985      * @type Mixed
42986      */
42987     waitMsgTarget : false,
42988
42989     // private
42990     initEl : function(el){
42991         this.el = Roo.get(el);
42992         this.id = this.el.id || Roo.id();
42993         this.el.on('submit', this.onSubmit, this);
42994         this.el.addClass('x-form');
42995     },
42996
42997     // private
42998     onSubmit : function(e){
42999         e.stopEvent();
43000     },
43001
43002     /**
43003      * Returns true if client-side validation on the form is successful.
43004      * @return Boolean
43005      */
43006     isValid : function(){
43007         var valid = true;
43008         this.items.each(function(f){
43009            if(!f.validate()){
43010                valid = false;
43011            }
43012         });
43013         return valid;
43014     },
43015
43016     /**
43017      * Returns true if any fields in this form have changed since their original load.
43018      * @return Boolean
43019      */
43020     isDirty : function(){
43021         var dirty = false;
43022         this.items.each(function(f){
43023            if(f.isDirty()){
43024                dirty = true;
43025                return false;
43026            }
43027         });
43028         return dirty;
43029     },
43030
43031     /**
43032      * Performs a predefined action (submit or load) or custom actions you define on this form.
43033      * @param {String} actionName The name of the action type
43034      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
43035      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
43036      * accept other config options):
43037      * <pre>
43038 Property          Type             Description
43039 ----------------  ---------------  ----------------------------------------------------------------------------------
43040 url               String           The url for the action (defaults to the form's url)
43041 method            String           The form method to use (defaults to the form's method, or POST if not defined)
43042 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
43043 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
43044                                    validate the form on the client (defaults to false)
43045      * </pre>
43046      * @return {BasicForm} this
43047      */
43048     doAction : function(action, options){
43049         if(typeof action == 'string'){
43050             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
43051         }
43052         if(this.fireEvent('beforeaction', this, action) !== false){
43053             this.beforeAction(action);
43054             action.run.defer(100, action);
43055         }
43056         return this;
43057     },
43058
43059     /**
43060      * Shortcut to do a submit action.
43061      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43062      * @return {BasicForm} this
43063      */
43064     submit : function(options){
43065         this.doAction('submit', options);
43066         return this;
43067     },
43068
43069     /**
43070      * Shortcut to do a load action.
43071      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43072      * @return {BasicForm} this
43073      */
43074     load : function(options){
43075         this.doAction('load', options);
43076         return this;
43077     },
43078
43079     /**
43080      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
43081      * @param {Record} record The record to edit
43082      * @return {BasicForm} this
43083      */
43084     updateRecord : function(record){
43085         record.beginEdit();
43086         var fs = record.fields;
43087         fs.each(function(f){
43088             var field = this.findField(f.name);
43089             if(field){
43090                 record.set(f.name, field.getValue());
43091             }
43092         }, this);
43093         record.endEdit();
43094         return this;
43095     },
43096
43097     /**
43098      * Loads an Roo.data.Record into this form.
43099      * @param {Record} record The record to load
43100      * @return {BasicForm} this
43101      */
43102     loadRecord : function(record){
43103         this.setValues(record.data);
43104         return this;
43105     },
43106
43107     // private
43108     beforeAction : function(action){
43109         var o = action.options;
43110         
43111        
43112         if(this.waitMsgTarget === true){
43113             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
43114         }else if(this.waitMsgTarget){
43115             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
43116             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
43117         }else {
43118             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
43119         }
43120          
43121     },
43122
43123     // private
43124     afterAction : function(action, success){
43125         this.activeAction = null;
43126         var o = action.options;
43127         
43128         if(this.waitMsgTarget === true){
43129             this.el.unmask();
43130         }else if(this.waitMsgTarget){
43131             this.waitMsgTarget.unmask();
43132         }else{
43133             Roo.MessageBox.updateProgress(1);
43134             Roo.MessageBox.hide();
43135         }
43136          
43137         if(success){
43138             if(o.reset){
43139                 this.reset();
43140             }
43141             Roo.callback(o.success, o.scope, [this, action]);
43142             this.fireEvent('actioncomplete', this, action);
43143             
43144         }else{
43145             
43146             // failure condition..
43147             // we have a scenario where updates need confirming.
43148             // eg. if a locking scenario exists..
43149             // we look for { errors : { needs_confirm : true }} in the response.
43150             if (
43151                 (typeof(action.result) != 'undefined')  &&
43152                 (typeof(action.result.errors) != 'undefined')  &&
43153                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43154            ){
43155                 var _t = this;
43156                 Roo.MessageBox.confirm(
43157                     "Change requires confirmation",
43158                     action.result.errorMsg,
43159                     function(r) {
43160                         if (r != 'yes') {
43161                             return;
43162                         }
43163                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43164                     }
43165                     
43166                 );
43167                 
43168                 
43169                 
43170                 return;
43171             }
43172             
43173             Roo.callback(o.failure, o.scope, [this, action]);
43174             // show an error message if no failed handler is set..
43175             if (!this.hasListener('actionfailed')) {
43176                 Roo.MessageBox.alert("Error",
43177                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43178                         action.result.errorMsg :
43179                         "Saving Failed, please check your entries or try again"
43180                 );
43181             }
43182             
43183             this.fireEvent('actionfailed', this, action);
43184         }
43185         
43186     },
43187
43188     /**
43189      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43190      * @param {String} id The value to search for
43191      * @return Field
43192      */
43193     findField : function(id){
43194         var field = this.items.get(id);
43195         if(!field){
43196             this.items.each(function(f){
43197                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43198                     field = f;
43199                     return false;
43200                 }
43201             });
43202         }
43203         return field || null;
43204     },
43205
43206     /**
43207      * Add a secondary form to this one, 
43208      * Used to provide tabbed forms. One form is primary, with hidden values 
43209      * which mirror the elements from the other forms.
43210      * 
43211      * @param {Roo.form.Form} form to add.
43212      * 
43213      */
43214     addForm : function(form)
43215     {
43216        
43217         if (this.childForms.indexOf(form) > -1) {
43218             // already added..
43219             return;
43220         }
43221         this.childForms.push(form);
43222         var n = '';
43223         Roo.each(form.allItems, function (fe) {
43224             
43225             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
43226             if (this.findField(n)) { // already added..
43227                 return;
43228             }
43229             var add = new Roo.form.Hidden({
43230                 name : n
43231             });
43232             add.render(this.el);
43233             
43234             this.add( add );
43235         }, this);
43236         
43237     },
43238     /**
43239      * Mark fields in this form invalid in bulk.
43240      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
43241      * @return {BasicForm} this
43242      */
43243     markInvalid : function(errors){
43244         if(errors instanceof Array){
43245             for(var i = 0, len = errors.length; i < len; i++){
43246                 var fieldError = errors[i];
43247                 var f = this.findField(fieldError.id);
43248                 if(f){
43249                     f.markInvalid(fieldError.msg);
43250                 }
43251             }
43252         }else{
43253             var field, id;
43254             for(id in errors){
43255                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
43256                     field.markInvalid(errors[id]);
43257                 }
43258             }
43259         }
43260         Roo.each(this.childForms || [], function (f) {
43261             f.markInvalid(errors);
43262         });
43263         
43264         return this;
43265     },
43266
43267     /**
43268      * Set values for fields in this form in bulk.
43269      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
43270      * @return {BasicForm} this
43271      */
43272     setValues : function(values){
43273         if(values instanceof Array){ // array of objects
43274             for(var i = 0, len = values.length; i < len; i++){
43275                 var v = values[i];
43276                 var f = this.findField(v.id);
43277                 if(f){
43278                     f.setValue(v.value);
43279                     if(this.trackResetOnLoad){
43280                         f.originalValue = f.getValue();
43281                     }
43282                 }
43283             }
43284         }else{ // object hash
43285             var field, id;
43286             for(id in values){
43287                 if(typeof values[id] != 'function' && (field = this.findField(id))){
43288                     
43289                     if (field.setFromData && 
43290                         field.valueField && 
43291                         field.displayField &&
43292                         // combos' with local stores can 
43293                         // be queried via setValue()
43294                         // to set their value..
43295                         (field.store && !field.store.isLocal)
43296                         ) {
43297                         // it's a combo
43298                         var sd = { };
43299                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
43300                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
43301                         field.setFromData(sd);
43302                         
43303                     } else {
43304                         field.setValue(values[id]);
43305                     }
43306                     
43307                     
43308                     if(this.trackResetOnLoad){
43309                         field.originalValue = field.getValue();
43310                     }
43311                 }
43312             }
43313         }
43314          
43315         Roo.each(this.childForms || [], function (f) {
43316             f.setValues(values);
43317         });
43318                 
43319         return this;
43320     },
43321
43322     /**
43323      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
43324      * they are returned as an array.
43325      * @param {Boolean} asString
43326      * @return {Object}
43327      */
43328     getValues : function(asString){
43329         if (this.childForms) {
43330             // copy values from the child forms
43331             Roo.each(this.childForms, function (f) {
43332                 this.setValues(f.getValues());
43333             }, this);
43334         }
43335         
43336         
43337         
43338         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
43339         if(asString === true){
43340             return fs;
43341         }
43342         return Roo.urlDecode(fs);
43343     },
43344     
43345     /**
43346      * Returns the fields in this form as an object with key/value pairs. 
43347      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
43348      * @return {Object}
43349      */
43350     getFieldValues : function(with_hidden)
43351     {
43352         if (this.childForms) {
43353             // copy values from the child forms
43354             // should this call getFieldValues - probably not as we do not currently copy
43355             // hidden fields when we generate..
43356             Roo.each(this.childForms, function (f) {
43357                 this.setValues(f.getValues());
43358             }, this);
43359         }
43360         
43361         var ret = {};
43362         this.items.each(function(f){
43363             if (!f.getName()) {
43364                 return;
43365             }
43366             var v = f.getValue();
43367             // not sure if this supported any more..
43368             if ((typeof(v) == 'object') && f.getRawValue) {
43369                 v = f.getRawValue() ; // dates..
43370             }
43371             // combo boxes where name != hiddenName...
43372             if (f.name != f.getName()) {
43373                 ret[f.name] = f.getRawValue();
43374             }
43375             ret[f.getName()] = v;
43376         });
43377         
43378         return ret;
43379     },
43380
43381     /**
43382      * Clears all invalid messages in this form.
43383      * @return {BasicForm} this
43384      */
43385     clearInvalid : function(){
43386         this.items.each(function(f){
43387            f.clearInvalid();
43388         });
43389         
43390         Roo.each(this.childForms || [], function (f) {
43391             f.clearInvalid();
43392         });
43393         
43394         
43395         return this;
43396     },
43397
43398     /**
43399      * Resets this form.
43400      * @return {BasicForm} this
43401      */
43402     reset : function(){
43403         this.items.each(function(f){
43404             f.reset();
43405         });
43406         
43407         Roo.each(this.childForms || [], function (f) {
43408             f.reset();
43409         });
43410        
43411         
43412         return this;
43413     },
43414
43415     /**
43416      * Add Roo.form components to this form.
43417      * @param {Field} field1
43418      * @param {Field} field2 (optional)
43419      * @param {Field} etc (optional)
43420      * @return {BasicForm} this
43421      */
43422     add : function(){
43423         this.items.addAll(Array.prototype.slice.call(arguments, 0));
43424         return this;
43425     },
43426
43427
43428     /**
43429      * Removes a field from the items collection (does NOT remove its markup).
43430      * @param {Field} field
43431      * @return {BasicForm} this
43432      */
43433     remove : function(field){
43434         this.items.remove(field);
43435         return this;
43436     },
43437
43438     /**
43439      * Looks at the fields in this form, checks them for an id attribute,
43440      * and calls applyTo on the existing dom element with that id.
43441      * @return {BasicForm} this
43442      */
43443     render : function(){
43444         this.items.each(function(f){
43445             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
43446                 f.applyTo(f.id);
43447             }
43448         });
43449         return this;
43450     },
43451
43452     /**
43453      * Calls {@link Ext#apply} for all fields in this form with the passed object.
43454      * @param {Object} values
43455      * @return {BasicForm} this
43456      */
43457     applyToFields : function(o){
43458         this.items.each(function(f){
43459            Roo.apply(f, o);
43460         });
43461         return this;
43462     },
43463
43464     /**
43465      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
43466      * @param {Object} values
43467      * @return {BasicForm} this
43468      */
43469     applyIfToFields : function(o){
43470         this.items.each(function(f){
43471            Roo.applyIf(f, o);
43472         });
43473         return this;
43474     }
43475 });
43476
43477 // back compat
43478 Roo.BasicForm = Roo.form.BasicForm;/*
43479  * Based on:
43480  * Ext JS Library 1.1.1
43481  * Copyright(c) 2006-2007, Ext JS, LLC.
43482  *
43483  * Originally Released Under LGPL - original licence link has changed is not relivant.
43484  *
43485  * Fork - LGPL
43486  * <script type="text/javascript">
43487  */
43488
43489 /**
43490  * @class Roo.form.Form
43491  * @extends Roo.form.BasicForm
43492  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
43493  * @constructor
43494  * @param {Object} config Configuration options
43495  */
43496 Roo.form.Form = function(config){
43497     var xitems =  [];
43498     if (config.items) {
43499         xitems = config.items;
43500         delete config.items;
43501     }
43502    
43503     
43504     Roo.form.Form.superclass.constructor.call(this, null, config);
43505     this.url = this.url || this.action;
43506     if(!this.root){
43507         this.root = new Roo.form.Layout(Roo.applyIf({
43508             id: Roo.id()
43509         }, config));
43510     }
43511     this.active = this.root;
43512     /**
43513      * Array of all the buttons that have been added to this form via {@link addButton}
43514      * @type Array
43515      */
43516     this.buttons = [];
43517     this.allItems = [];
43518     this.addEvents({
43519         /**
43520          * @event clientvalidation
43521          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
43522          * @param {Form} this
43523          * @param {Boolean} valid true if the form has passed client-side validation
43524          */
43525         clientvalidation: true,
43526         /**
43527          * @event rendered
43528          * Fires when the form is rendered
43529          * @param {Roo.form.Form} form
43530          */
43531         rendered : true
43532     });
43533     
43534     if (this.progressUrl) {
43535             // push a hidden field onto the list of fields..
43536             this.addxtype( {
43537                     xns: Roo.form, 
43538                     xtype : 'Hidden', 
43539                     name : 'UPLOAD_IDENTIFIER' 
43540             });
43541         }
43542         
43543     
43544     Roo.each(xitems, this.addxtype, this);
43545     
43546     
43547     
43548 };
43549
43550 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
43551     /**
43552      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
43553      */
43554     /**
43555      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
43556      */
43557     /**
43558      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
43559      */
43560     buttonAlign:'center',
43561
43562     /**
43563      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
43564      */
43565     minButtonWidth:75,
43566
43567     /**
43568      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
43569      * This property cascades to child containers if not set.
43570      */
43571     labelAlign:'left',
43572
43573     /**
43574      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
43575      * fires a looping event with that state. This is required to bind buttons to the valid
43576      * state using the config value formBind:true on the button.
43577      */
43578     monitorValid : false,
43579
43580     /**
43581      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
43582      */
43583     monitorPoll : 200,
43584     
43585     /**
43586      * @cfg {String} progressUrl - Url to return progress data 
43587      */
43588     
43589     progressUrl : false,
43590   
43591     /**
43592      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
43593      * fields are added and the column is closed. If no fields are passed the column remains open
43594      * until end() is called.
43595      * @param {Object} config The config to pass to the column
43596      * @param {Field} field1 (optional)
43597      * @param {Field} field2 (optional)
43598      * @param {Field} etc (optional)
43599      * @return Column The column container object
43600      */
43601     column : function(c){
43602         var col = new Roo.form.Column(c);
43603         this.start(col);
43604         if(arguments.length > 1){ // duplicate code required because of Opera
43605             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43606             this.end();
43607         }
43608         return col;
43609     },
43610
43611     /**
43612      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
43613      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
43614      * until end() is called.
43615      * @param {Object} config The config to pass to the fieldset
43616      * @param {Field} field1 (optional)
43617      * @param {Field} field2 (optional)
43618      * @param {Field} etc (optional)
43619      * @return FieldSet The fieldset container object
43620      */
43621     fieldset : function(c){
43622         var fs = new Roo.form.FieldSet(c);
43623         this.start(fs);
43624         if(arguments.length > 1){ // duplicate code required because of Opera
43625             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43626             this.end();
43627         }
43628         return fs;
43629     },
43630
43631     /**
43632      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
43633      * fields are added and the container is closed. If no fields are passed the container remains open
43634      * until end() is called.
43635      * @param {Object} config The config to pass to the Layout
43636      * @param {Field} field1 (optional)
43637      * @param {Field} field2 (optional)
43638      * @param {Field} etc (optional)
43639      * @return Layout The container object
43640      */
43641     container : function(c){
43642         var l = new Roo.form.Layout(c);
43643         this.start(l);
43644         if(arguments.length > 1){ // duplicate code required because of Opera
43645             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43646             this.end();
43647         }
43648         return l;
43649     },
43650
43651     /**
43652      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
43653      * @param {Object} container A Roo.form.Layout or subclass of Layout
43654      * @return {Form} this
43655      */
43656     start : function(c){
43657         // cascade label info
43658         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
43659         this.active.stack.push(c);
43660         c.ownerCt = this.active;
43661         this.active = c;
43662         return this;
43663     },
43664
43665     /**
43666      * Closes the current open container
43667      * @return {Form} this
43668      */
43669     end : function(){
43670         if(this.active == this.root){
43671             return this;
43672         }
43673         this.active = this.active.ownerCt;
43674         return this;
43675     },
43676
43677     /**
43678      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
43679      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
43680      * as the label of the field.
43681      * @param {Field} field1
43682      * @param {Field} field2 (optional)
43683      * @param {Field} etc. (optional)
43684      * @return {Form} this
43685      */
43686     add : function(){
43687         this.active.stack.push.apply(this.active.stack, arguments);
43688         this.allItems.push.apply(this.allItems,arguments);
43689         var r = [];
43690         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
43691             if(a[i].isFormField){
43692                 r.push(a[i]);
43693             }
43694         }
43695         if(r.length > 0){
43696             Roo.form.Form.superclass.add.apply(this, r);
43697         }
43698         return this;
43699     },
43700     
43701
43702     
43703     
43704     
43705      /**
43706      * Find any element that has been added to a form, using it's ID or name
43707      * This can include framesets, columns etc. along with regular fields..
43708      * @param {String} id - id or name to find.
43709      
43710      * @return {Element} e - or false if nothing found.
43711      */
43712     findbyId : function(id)
43713     {
43714         var ret = false;
43715         if (!id) {
43716             return ret;
43717         }
43718         Roo.each(this.allItems, function(f){
43719             if (f.id == id || f.name == id ){
43720                 ret = f;
43721                 return false;
43722             }
43723         });
43724         return ret;
43725     },
43726
43727     
43728     
43729     /**
43730      * Render this form into the passed container. This should only be called once!
43731      * @param {String/HTMLElement/Element} container The element this component should be rendered into
43732      * @return {Form} this
43733      */
43734     render : function(ct)
43735     {
43736         
43737         
43738         
43739         ct = Roo.get(ct);
43740         var o = this.autoCreate || {
43741             tag: 'form',
43742             method : this.method || 'POST',
43743             id : this.id || Roo.id()
43744         };
43745         this.initEl(ct.createChild(o));
43746
43747         this.root.render(this.el);
43748         
43749        
43750              
43751         this.items.each(function(f){
43752             f.render('x-form-el-'+f.id);
43753         });
43754
43755         if(this.buttons.length > 0){
43756             // tables are required to maintain order and for correct IE layout
43757             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
43758                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
43759                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
43760             }}, null, true);
43761             var tr = tb.getElementsByTagName('tr')[0];
43762             for(var i = 0, len = this.buttons.length; i < len; i++) {
43763                 var b = this.buttons[i];
43764                 var td = document.createElement('td');
43765                 td.className = 'x-form-btn-td';
43766                 b.render(tr.appendChild(td));
43767             }
43768         }
43769         if(this.monitorValid){ // initialize after render
43770             this.startMonitoring();
43771         }
43772         this.fireEvent('rendered', this);
43773         return this;
43774     },
43775
43776     /**
43777      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
43778      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
43779      * object or a valid Roo.DomHelper element config
43780      * @param {Function} handler The function called when the button is clicked
43781      * @param {Object} scope (optional) The scope of the handler function
43782      * @return {Roo.Button}
43783      */
43784     addButton : function(config, handler, scope){
43785         var bc = {
43786             handler: handler,
43787             scope: scope,
43788             minWidth: this.minButtonWidth,
43789             hideParent:true
43790         };
43791         if(typeof config == "string"){
43792             bc.text = config;
43793         }else{
43794             Roo.apply(bc, config);
43795         }
43796         var btn = new Roo.Button(null, bc);
43797         this.buttons.push(btn);
43798         return btn;
43799     },
43800
43801      /**
43802      * Adds a series of form elements (using the xtype property as the factory method.
43803      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
43804      * @param {Object} config 
43805      */
43806     
43807     addxtype : function()
43808     {
43809         var ar = Array.prototype.slice.call(arguments, 0);
43810         var ret = false;
43811         for(var i = 0; i < ar.length; i++) {
43812             if (!ar[i]) {
43813                 continue; // skip -- if this happends something invalid got sent, we 
43814                 // should ignore it, as basically that interface element will not show up
43815                 // and that should be pretty obvious!!
43816             }
43817             
43818             if (Roo.form[ar[i].xtype]) {
43819                 ar[i].form = this;
43820                 var fe = Roo.factory(ar[i], Roo.form);
43821                 if (!ret) {
43822                     ret = fe;
43823                 }
43824                 fe.form = this;
43825                 if (fe.store) {
43826                     fe.store.form = this;
43827                 }
43828                 if (fe.isLayout) {  
43829                          
43830                     this.start(fe);
43831                     this.allItems.push(fe);
43832                     if (fe.items && fe.addxtype) {
43833                         fe.addxtype.apply(fe, fe.items);
43834                         delete fe.items;
43835                     }
43836                      this.end();
43837                     continue;
43838                 }
43839                 
43840                 
43841                  
43842                 this.add(fe);
43843               //  console.log('adding ' + ar[i].xtype);
43844             }
43845             if (ar[i].xtype == 'Button') {  
43846                 //console.log('adding button');
43847                 //console.log(ar[i]);
43848                 this.addButton(ar[i]);
43849                 this.allItems.push(fe);
43850                 continue;
43851             }
43852             
43853             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
43854                 alert('end is not supported on xtype any more, use items');
43855             //    this.end();
43856             //    //console.log('adding end');
43857             }
43858             
43859         }
43860         return ret;
43861     },
43862     
43863     /**
43864      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
43865      * option "monitorValid"
43866      */
43867     startMonitoring : function(){
43868         if(!this.bound){
43869             this.bound = true;
43870             Roo.TaskMgr.start({
43871                 run : this.bindHandler,
43872                 interval : this.monitorPoll || 200,
43873                 scope: this
43874             });
43875         }
43876     },
43877
43878     /**
43879      * Stops monitoring of the valid state of this form
43880      */
43881     stopMonitoring : function(){
43882         this.bound = false;
43883     },
43884
43885     // private
43886     bindHandler : function(){
43887         if(!this.bound){
43888             return false; // stops binding
43889         }
43890         var valid = true;
43891         this.items.each(function(f){
43892             if(!f.isValid(true)){
43893                 valid = false;
43894                 return false;
43895             }
43896         });
43897         for(var i = 0, len = this.buttons.length; i < len; i++){
43898             var btn = this.buttons[i];
43899             if(btn.formBind === true && btn.disabled === valid){
43900                 btn.setDisabled(!valid);
43901             }
43902         }
43903         this.fireEvent('clientvalidation', this, valid);
43904     }
43905     
43906     
43907     
43908     
43909     
43910     
43911     
43912     
43913 });
43914
43915
43916 // back compat
43917 Roo.Form = Roo.form.Form;
43918 /*
43919  * Based on:
43920  * Ext JS Library 1.1.1
43921  * Copyright(c) 2006-2007, Ext JS, LLC.
43922  *
43923  * Originally Released Under LGPL - original licence link has changed is not relivant.
43924  *
43925  * Fork - LGPL
43926  * <script type="text/javascript">
43927  */
43928  
43929  /**
43930  * @class Roo.form.Action
43931  * Internal Class used to handle form actions
43932  * @constructor
43933  * @param {Roo.form.BasicForm} el The form element or its id
43934  * @param {Object} config Configuration options
43935  */
43936  
43937  
43938 // define the action interface
43939 Roo.form.Action = function(form, options){
43940     this.form = form;
43941     this.options = options || {};
43942 };
43943 /**
43944  * Client Validation Failed
43945  * @const 
43946  */
43947 Roo.form.Action.CLIENT_INVALID = 'client';
43948 /**
43949  * Server Validation Failed
43950  * @const 
43951  */
43952  Roo.form.Action.SERVER_INVALID = 'server';
43953  /**
43954  * Connect to Server Failed
43955  * @const 
43956  */
43957 Roo.form.Action.CONNECT_FAILURE = 'connect';
43958 /**
43959  * Reading Data from Server Failed
43960  * @const 
43961  */
43962 Roo.form.Action.LOAD_FAILURE = 'load';
43963
43964 Roo.form.Action.prototype = {
43965     type : 'default',
43966     failureType : undefined,
43967     response : undefined,
43968     result : undefined,
43969
43970     // interface method
43971     run : function(options){
43972
43973     },
43974
43975     // interface method
43976     success : function(response){
43977
43978     },
43979
43980     // interface method
43981     handleResponse : function(response){
43982
43983     },
43984
43985     // default connection failure
43986     failure : function(response){
43987         
43988         this.response = response;
43989         this.failureType = Roo.form.Action.CONNECT_FAILURE;
43990         this.form.afterAction(this, false);
43991     },
43992
43993     processResponse : function(response){
43994         this.response = response;
43995         if(!response.responseText){
43996             return true;
43997         }
43998         this.result = this.handleResponse(response);
43999         return this.result;
44000     },
44001
44002     // utility functions used internally
44003     getUrl : function(appendParams){
44004         var url = this.options.url || this.form.url || this.form.el.dom.action;
44005         if(appendParams){
44006             var p = this.getParams();
44007             if(p){
44008                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
44009             }
44010         }
44011         return url;
44012     },
44013
44014     getMethod : function(){
44015         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
44016     },
44017
44018     getParams : function(){
44019         var bp = this.form.baseParams;
44020         var p = this.options.params;
44021         if(p){
44022             if(typeof p == "object"){
44023                 p = Roo.urlEncode(Roo.applyIf(p, bp));
44024             }else if(typeof p == 'string' && bp){
44025                 p += '&' + Roo.urlEncode(bp);
44026             }
44027         }else if(bp){
44028             p = Roo.urlEncode(bp);
44029         }
44030         return p;
44031     },
44032
44033     createCallback : function(){
44034         return {
44035             success: this.success,
44036             failure: this.failure,
44037             scope: this,
44038             timeout: (this.form.timeout*1000),
44039             upload: this.form.fileUpload ? this.success : undefined
44040         };
44041     }
44042 };
44043
44044 Roo.form.Action.Submit = function(form, options){
44045     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
44046 };
44047
44048 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
44049     type : 'submit',
44050
44051     haveProgress : false,
44052     uploadComplete : false,
44053     
44054     // uploadProgress indicator.
44055     uploadProgress : function()
44056     {
44057         if (!this.form.progressUrl) {
44058             return;
44059         }
44060         
44061         if (!this.haveProgress) {
44062             Roo.MessageBox.progress("Uploading", "Uploading");
44063         }
44064         if (this.uploadComplete) {
44065            Roo.MessageBox.hide();
44066            return;
44067         }
44068         
44069         this.haveProgress = true;
44070    
44071         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
44072         
44073         var c = new Roo.data.Connection();
44074         c.request({
44075             url : this.form.progressUrl,
44076             params: {
44077                 id : uid
44078             },
44079             method: 'GET',
44080             success : function(req){
44081                //console.log(data);
44082                 var rdata = false;
44083                 var edata;
44084                 try  {
44085                    rdata = Roo.decode(req.responseText)
44086                 } catch (e) {
44087                     Roo.log("Invalid data from server..");
44088                     Roo.log(edata);
44089                     return;
44090                 }
44091                 if (!rdata || !rdata.success) {
44092                     Roo.log(rdata);
44093                     Roo.MessageBox.alert(Roo.encode(rdata));
44094                     return;
44095                 }
44096                 var data = rdata.data;
44097                 
44098                 if (this.uploadComplete) {
44099                    Roo.MessageBox.hide();
44100                    return;
44101                 }
44102                    
44103                 if (data){
44104                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
44105                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
44106                     );
44107                 }
44108                 this.uploadProgress.defer(2000,this);
44109             },
44110        
44111             failure: function(data) {
44112                 Roo.log('progress url failed ');
44113                 Roo.log(data);
44114             },
44115             scope : this
44116         });
44117            
44118     },
44119     
44120     
44121     run : function()
44122     {
44123         // run get Values on the form, so it syncs any secondary forms.
44124         this.form.getValues();
44125         
44126         var o = this.options;
44127         var method = this.getMethod();
44128         var isPost = method == 'POST';
44129         if(o.clientValidation === false || this.form.isValid()){
44130             
44131             if (this.form.progressUrl) {
44132                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
44133                     (new Date() * 1) + '' + Math.random());
44134                     
44135             } 
44136             
44137             
44138             Roo.Ajax.request(Roo.apply(this.createCallback(), {
44139                 form:this.form.el.dom,
44140                 url:this.getUrl(!isPost),
44141                 method: method,
44142                 params:isPost ? this.getParams() : null,
44143                 isUpload: this.form.fileUpload
44144             }));
44145             
44146             this.uploadProgress();
44147
44148         }else if (o.clientValidation !== false){ // client validation failed
44149             this.failureType = Roo.form.Action.CLIENT_INVALID;
44150             this.form.afterAction(this, false);
44151         }
44152     },
44153
44154     success : function(response)
44155     {
44156         this.uploadComplete= true;
44157         if (this.haveProgress) {
44158             Roo.MessageBox.hide();
44159         }
44160         
44161         
44162         var result = this.processResponse(response);
44163         if(result === true || result.success){
44164             this.form.afterAction(this, true);
44165             return;
44166         }
44167         if(result.errors){
44168             this.form.markInvalid(result.errors);
44169             this.failureType = Roo.form.Action.SERVER_INVALID;
44170         }
44171         this.form.afterAction(this, false);
44172     },
44173     failure : function(response)
44174     {
44175         this.uploadComplete= true;
44176         if (this.haveProgress) {
44177             Roo.MessageBox.hide();
44178         }
44179         
44180         this.response = response;
44181         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44182         this.form.afterAction(this, false);
44183     },
44184     
44185     handleResponse : function(response){
44186         if(this.form.errorReader){
44187             var rs = this.form.errorReader.read(response);
44188             var errors = [];
44189             if(rs.records){
44190                 for(var i = 0, len = rs.records.length; i < len; i++) {
44191                     var r = rs.records[i];
44192                     errors[i] = r.data;
44193                 }
44194             }
44195             if(errors.length < 1){
44196                 errors = null;
44197             }
44198             return {
44199                 success : rs.success,
44200                 errors : errors
44201             };
44202         }
44203         var ret = false;
44204         try {
44205             ret = Roo.decode(response.responseText);
44206         } catch (e) {
44207             ret = {
44208                 success: false,
44209                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
44210                 errors : []
44211             };
44212         }
44213         return ret;
44214         
44215     }
44216 });
44217
44218
44219 Roo.form.Action.Load = function(form, options){
44220     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
44221     this.reader = this.form.reader;
44222 };
44223
44224 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
44225     type : 'load',
44226
44227     run : function(){
44228         
44229         Roo.Ajax.request(Roo.apply(
44230                 this.createCallback(), {
44231                     method:this.getMethod(),
44232                     url:this.getUrl(false),
44233                     params:this.getParams()
44234         }));
44235     },
44236
44237     success : function(response){
44238         
44239         var result = this.processResponse(response);
44240         if(result === true || !result.success || !result.data){
44241             this.failureType = Roo.form.Action.LOAD_FAILURE;
44242             this.form.afterAction(this, false);
44243             return;
44244         }
44245         this.form.clearInvalid();
44246         this.form.setValues(result.data);
44247         this.form.afterAction(this, true);
44248     },
44249
44250     handleResponse : function(response){
44251         if(this.form.reader){
44252             var rs = this.form.reader.read(response);
44253             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
44254             return {
44255                 success : rs.success,
44256                 data : data
44257             };
44258         }
44259         return Roo.decode(response.responseText);
44260     }
44261 });
44262
44263 Roo.form.Action.ACTION_TYPES = {
44264     'load' : Roo.form.Action.Load,
44265     'submit' : Roo.form.Action.Submit
44266 };/*
44267  * Based on:
44268  * Ext JS Library 1.1.1
44269  * Copyright(c) 2006-2007, Ext JS, LLC.
44270  *
44271  * Originally Released Under LGPL - original licence link has changed is not relivant.
44272  *
44273  * Fork - LGPL
44274  * <script type="text/javascript">
44275  */
44276  
44277 /**
44278  * @class Roo.form.Layout
44279  * @extends Roo.Component
44280  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
44281  * @constructor
44282  * @param {Object} config Configuration options
44283  */
44284 Roo.form.Layout = function(config){
44285     var xitems = [];
44286     if (config.items) {
44287         xitems = config.items;
44288         delete config.items;
44289     }
44290     Roo.form.Layout.superclass.constructor.call(this, config);
44291     this.stack = [];
44292     Roo.each(xitems, this.addxtype, this);
44293      
44294 };
44295
44296 Roo.extend(Roo.form.Layout, Roo.Component, {
44297     /**
44298      * @cfg {String/Object} autoCreate
44299      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
44300      */
44301     /**
44302      * @cfg {String/Object/Function} style
44303      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
44304      * a function which returns such a specification.
44305      */
44306     /**
44307      * @cfg {String} labelAlign
44308      * Valid values are "left," "top" and "right" (defaults to "left")
44309      */
44310     /**
44311      * @cfg {Number} labelWidth
44312      * Fixed width in pixels of all field labels (defaults to undefined)
44313      */
44314     /**
44315      * @cfg {Boolean} clear
44316      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
44317      */
44318     clear : true,
44319     /**
44320      * @cfg {String} labelSeparator
44321      * The separator to use after field labels (defaults to ':')
44322      */
44323     labelSeparator : ':',
44324     /**
44325      * @cfg {Boolean} hideLabels
44326      * True to suppress the display of field labels in this layout (defaults to false)
44327      */
44328     hideLabels : false,
44329
44330     // private
44331     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
44332     
44333     isLayout : true,
44334     
44335     // private
44336     onRender : function(ct, position){
44337         if(this.el){ // from markup
44338             this.el = Roo.get(this.el);
44339         }else {  // generate
44340             var cfg = this.getAutoCreate();
44341             this.el = ct.createChild(cfg, position);
44342         }
44343         if(this.style){
44344             this.el.applyStyles(this.style);
44345         }
44346         if(this.labelAlign){
44347             this.el.addClass('x-form-label-'+this.labelAlign);
44348         }
44349         if(this.hideLabels){
44350             this.labelStyle = "display:none";
44351             this.elementStyle = "padding-left:0;";
44352         }else{
44353             if(typeof this.labelWidth == 'number'){
44354                 this.labelStyle = "width:"+this.labelWidth+"px;";
44355                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
44356             }
44357             if(this.labelAlign == 'top'){
44358                 this.labelStyle = "width:auto;";
44359                 this.elementStyle = "padding-left:0;";
44360             }
44361         }
44362         var stack = this.stack;
44363         var slen = stack.length;
44364         if(slen > 0){
44365             if(!this.fieldTpl){
44366                 var t = new Roo.Template(
44367                     '<div class="x-form-item {5}">',
44368                         '<label for="{0}" style="{2}">{1}{4}</label>',
44369                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44370                         '</div>',
44371                     '</div><div class="x-form-clear-left"></div>'
44372                 );
44373                 t.disableFormats = true;
44374                 t.compile();
44375                 Roo.form.Layout.prototype.fieldTpl = t;
44376             }
44377             for(var i = 0; i < slen; i++) {
44378                 if(stack[i].isFormField){
44379                     this.renderField(stack[i]);
44380                 }else{
44381                     this.renderComponent(stack[i]);
44382                 }
44383             }
44384         }
44385         if(this.clear){
44386             this.el.createChild({cls:'x-form-clear'});
44387         }
44388     },
44389
44390     // private
44391     renderField : function(f){
44392         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
44393                f.id, //0
44394                f.fieldLabel, //1
44395                f.labelStyle||this.labelStyle||'', //2
44396                this.elementStyle||'', //3
44397                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
44398                f.itemCls||this.itemCls||''  //5
44399        ], true).getPrevSibling());
44400     },
44401
44402     // private
44403     renderComponent : function(c){
44404         c.render(c.isLayout ? this.el : this.el.createChild());    
44405     },
44406     /**
44407      * Adds a object form elements (using the xtype property as the factory method.)
44408      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
44409      * @param {Object} config 
44410      */
44411     addxtype : function(o)
44412     {
44413         // create the lement.
44414         o.form = this.form;
44415         var fe = Roo.factory(o, Roo.form);
44416         this.form.allItems.push(fe);
44417         this.stack.push(fe);
44418         
44419         if (fe.isFormField) {
44420             this.form.items.add(fe);
44421         }
44422          
44423         return fe;
44424     }
44425 });
44426
44427 /**
44428  * @class Roo.form.Column
44429  * @extends Roo.form.Layout
44430  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
44431  * @constructor
44432  * @param {Object} config Configuration options
44433  */
44434 Roo.form.Column = function(config){
44435     Roo.form.Column.superclass.constructor.call(this, config);
44436 };
44437
44438 Roo.extend(Roo.form.Column, Roo.form.Layout, {
44439     /**
44440      * @cfg {Number/String} width
44441      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44442      */
44443     /**
44444      * @cfg {String/Object} autoCreate
44445      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
44446      */
44447
44448     // private
44449     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
44450
44451     // private
44452     onRender : function(ct, position){
44453         Roo.form.Column.superclass.onRender.call(this, ct, position);
44454         if(this.width){
44455             this.el.setWidth(this.width);
44456         }
44457     }
44458 });
44459
44460
44461 /**
44462  * @class Roo.form.Row
44463  * @extends Roo.form.Layout
44464  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
44465  * @constructor
44466  * @param {Object} config Configuration options
44467  */
44468
44469  
44470 Roo.form.Row = function(config){
44471     Roo.form.Row.superclass.constructor.call(this, config);
44472 };
44473  
44474 Roo.extend(Roo.form.Row, Roo.form.Layout, {
44475       /**
44476      * @cfg {Number/String} width
44477      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44478      */
44479     /**
44480      * @cfg {Number/String} height
44481      * The fixed height of the column in pixels or CSS value (defaults to "auto")
44482      */
44483     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
44484     
44485     padWidth : 20,
44486     // private
44487     onRender : function(ct, position){
44488         //console.log('row render');
44489         if(!this.rowTpl){
44490             var t = new Roo.Template(
44491                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
44492                     '<label for="{0}" style="{2}">{1}{4}</label>',
44493                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44494                     '</div>',
44495                 '</div>'
44496             );
44497             t.disableFormats = true;
44498             t.compile();
44499             Roo.form.Layout.prototype.rowTpl = t;
44500         }
44501         this.fieldTpl = this.rowTpl;
44502         
44503         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
44504         var labelWidth = 100;
44505         
44506         if ((this.labelAlign != 'top')) {
44507             if (typeof this.labelWidth == 'number') {
44508                 labelWidth = this.labelWidth
44509             }
44510             this.padWidth =  20 + labelWidth;
44511             
44512         }
44513         
44514         Roo.form.Column.superclass.onRender.call(this, ct, position);
44515         if(this.width){
44516             this.el.setWidth(this.width);
44517         }
44518         if(this.height){
44519             this.el.setHeight(this.height);
44520         }
44521     },
44522     
44523     // private
44524     renderField : function(f){
44525         f.fieldEl = this.fieldTpl.append(this.el, [
44526                f.id, f.fieldLabel,
44527                f.labelStyle||this.labelStyle||'',
44528                this.elementStyle||'',
44529                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
44530                f.itemCls||this.itemCls||'',
44531                f.width ? f.width + this.padWidth : 160 + this.padWidth
44532        ],true);
44533     }
44534 });
44535  
44536
44537 /**
44538  * @class Roo.form.FieldSet
44539  * @extends Roo.form.Layout
44540  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
44541  * @constructor
44542  * @param {Object} config Configuration options
44543  */
44544 Roo.form.FieldSet = function(config){
44545     Roo.form.FieldSet.superclass.constructor.call(this, config);
44546 };
44547
44548 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
44549     /**
44550      * @cfg {String} legend
44551      * The text to display as the legend for the FieldSet (defaults to '')
44552      */
44553     /**
44554      * @cfg {String/Object} autoCreate
44555      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
44556      */
44557
44558     // private
44559     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
44560
44561     // private
44562     onRender : function(ct, position){
44563         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
44564         if(this.legend){
44565             this.setLegend(this.legend);
44566         }
44567     },
44568
44569     // private
44570     setLegend : function(text){
44571         if(this.rendered){
44572             this.el.child('legend').update(text);
44573         }
44574     }
44575 });/*
44576  * Based on:
44577  * Ext JS Library 1.1.1
44578  * Copyright(c) 2006-2007, Ext JS, LLC.
44579  *
44580  * Originally Released Under LGPL - original licence link has changed is not relivant.
44581  *
44582  * Fork - LGPL
44583  * <script type="text/javascript">
44584  */
44585 /**
44586  * @class Roo.form.VTypes
44587  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
44588  * @singleton
44589  */
44590 Roo.form.VTypes = function(){
44591     // closure these in so they are only created once.
44592     var alpha = /^[a-zA-Z_]+$/;
44593     var alphanum = /^[a-zA-Z0-9_]+$/;
44594     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
44595     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
44596
44597     // All these messages and functions are configurable
44598     return {
44599         /**
44600          * The function used to validate email addresses
44601          * @param {String} value The email address
44602          */
44603         'email' : function(v){
44604             return email.test(v);
44605         },
44606         /**
44607          * The error text to display when the email validation function returns false
44608          * @type String
44609          */
44610         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
44611         /**
44612          * The keystroke filter mask to be applied on email input
44613          * @type RegExp
44614          */
44615         'emailMask' : /[a-z0-9_\.\-@]/i,
44616
44617         /**
44618          * The function used to validate URLs
44619          * @param {String} value The URL
44620          */
44621         'url' : function(v){
44622             return url.test(v);
44623         },
44624         /**
44625          * The error text to display when the url validation function returns false
44626          * @type String
44627          */
44628         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
44629         
44630         /**
44631          * The function used to validate alpha values
44632          * @param {String} value The value
44633          */
44634         'alpha' : function(v){
44635             return alpha.test(v);
44636         },
44637         /**
44638          * The error text to display when the alpha validation function returns false
44639          * @type String
44640          */
44641         'alphaText' : 'This field should only contain letters and _',
44642         /**
44643          * The keystroke filter mask to be applied on alpha input
44644          * @type RegExp
44645          */
44646         'alphaMask' : /[a-z_]/i,
44647
44648         /**
44649          * The function used to validate alphanumeric values
44650          * @param {String} value The value
44651          */
44652         'alphanum' : function(v){
44653             return alphanum.test(v);
44654         },
44655         /**
44656          * The error text to display when the alphanumeric validation function returns false
44657          * @type String
44658          */
44659         'alphanumText' : 'This field should only contain letters, numbers and _',
44660         /**
44661          * The keystroke filter mask to be applied on alphanumeric input
44662          * @type RegExp
44663          */
44664         'alphanumMask' : /[a-z0-9_]/i
44665     };
44666 }();//<script type="text/javascript">
44667
44668 /**
44669  * @class Roo.form.FCKeditor
44670  * @extends Roo.form.TextArea
44671  * Wrapper around the FCKEditor http://www.fckeditor.net
44672  * @constructor
44673  * Creates a new FCKeditor
44674  * @param {Object} config Configuration options
44675  */
44676 Roo.form.FCKeditor = function(config){
44677     Roo.form.FCKeditor.superclass.constructor.call(this, config);
44678     this.addEvents({
44679          /**
44680          * @event editorinit
44681          * Fired when the editor is initialized - you can add extra handlers here..
44682          * @param {FCKeditor} this
44683          * @param {Object} the FCK object.
44684          */
44685         editorinit : true
44686     });
44687     
44688     
44689 };
44690 Roo.form.FCKeditor.editors = { };
44691 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
44692 {
44693     //defaultAutoCreate : {
44694     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
44695     //},
44696     // private
44697     /**
44698      * @cfg {Object} fck options - see fck manual for details.
44699      */
44700     fckconfig : false,
44701     
44702     /**
44703      * @cfg {Object} fck toolbar set (Basic or Default)
44704      */
44705     toolbarSet : 'Basic',
44706     /**
44707      * @cfg {Object} fck BasePath
44708      */ 
44709     basePath : '/fckeditor/',
44710     
44711     
44712     frame : false,
44713     
44714     value : '',
44715     
44716    
44717     onRender : function(ct, position)
44718     {
44719         if(!this.el){
44720             this.defaultAutoCreate = {
44721                 tag: "textarea",
44722                 style:"width:300px;height:60px;",
44723                 autocomplete: "off"
44724             };
44725         }
44726         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
44727         /*
44728         if(this.grow){
44729             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
44730             if(this.preventScrollbars){
44731                 this.el.setStyle("overflow", "hidden");
44732             }
44733             this.el.setHeight(this.growMin);
44734         }
44735         */
44736         //console.log('onrender' + this.getId() );
44737         Roo.form.FCKeditor.editors[this.getId()] = this;
44738          
44739
44740         this.replaceTextarea() ;
44741         
44742     },
44743     
44744     getEditor : function() {
44745         return this.fckEditor;
44746     },
44747     /**
44748      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
44749      * @param {Mixed} value The value to set
44750      */
44751     
44752     
44753     setValue : function(value)
44754     {
44755         //console.log('setValue: ' + value);
44756         
44757         if(typeof(value) == 'undefined') { // not sure why this is happending...
44758             return;
44759         }
44760         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44761         
44762         //if(!this.el || !this.getEditor()) {
44763         //    this.value = value;
44764             //this.setValue.defer(100,this,[value]);    
44765         //    return;
44766         //} 
44767         
44768         if(!this.getEditor()) {
44769             return;
44770         }
44771         
44772         this.getEditor().SetData(value);
44773         
44774         //
44775
44776     },
44777
44778     /**
44779      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
44780      * @return {Mixed} value The field value
44781      */
44782     getValue : function()
44783     {
44784         
44785         if (this.frame && this.frame.dom.style.display == 'none') {
44786             return Roo.form.FCKeditor.superclass.getValue.call(this);
44787         }
44788         
44789         if(!this.el || !this.getEditor()) {
44790            
44791            // this.getValue.defer(100,this); 
44792             return this.value;
44793         }
44794        
44795         
44796         var value=this.getEditor().GetData();
44797         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44798         return Roo.form.FCKeditor.superclass.getValue.call(this);
44799         
44800
44801     },
44802
44803     /**
44804      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
44805      * @return {Mixed} value The field value
44806      */
44807     getRawValue : function()
44808     {
44809         if (this.frame && this.frame.dom.style.display == 'none') {
44810             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44811         }
44812         
44813         if(!this.el || !this.getEditor()) {
44814             //this.getRawValue.defer(100,this); 
44815             return this.value;
44816             return;
44817         }
44818         
44819         
44820         
44821         var value=this.getEditor().GetData();
44822         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
44823         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44824          
44825     },
44826     
44827     setSize : function(w,h) {
44828         
44829         
44830         
44831         //if (this.frame && this.frame.dom.style.display == 'none') {
44832         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44833         //    return;
44834         //}
44835         //if(!this.el || !this.getEditor()) {
44836         //    this.setSize.defer(100,this, [w,h]); 
44837         //    return;
44838         //}
44839         
44840         
44841         
44842         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44843         
44844         this.frame.dom.setAttribute('width', w);
44845         this.frame.dom.setAttribute('height', h);
44846         this.frame.setSize(w,h);
44847         
44848     },
44849     
44850     toggleSourceEdit : function(value) {
44851         
44852       
44853          
44854         this.el.dom.style.display = value ? '' : 'none';
44855         this.frame.dom.style.display = value ?  'none' : '';
44856         
44857     },
44858     
44859     
44860     focus: function(tag)
44861     {
44862         if (this.frame.dom.style.display == 'none') {
44863             return Roo.form.FCKeditor.superclass.focus.call(this);
44864         }
44865         if(!this.el || !this.getEditor()) {
44866             this.focus.defer(100,this, [tag]); 
44867             return;
44868         }
44869         
44870         
44871         
44872         
44873         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
44874         this.getEditor().Focus();
44875         if (tgs.length) {
44876             if (!this.getEditor().Selection.GetSelection()) {
44877                 this.focus.defer(100,this, [tag]); 
44878                 return;
44879             }
44880             
44881             
44882             var r = this.getEditor().EditorDocument.createRange();
44883             r.setStart(tgs[0],0);
44884             r.setEnd(tgs[0],0);
44885             this.getEditor().Selection.GetSelection().removeAllRanges();
44886             this.getEditor().Selection.GetSelection().addRange(r);
44887             this.getEditor().Focus();
44888         }
44889         
44890     },
44891     
44892     
44893     
44894     replaceTextarea : function()
44895     {
44896         if ( document.getElementById( this.getId() + '___Frame' ) )
44897             return ;
44898         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
44899         //{
44900             // We must check the elements firstly using the Id and then the name.
44901         var oTextarea = document.getElementById( this.getId() );
44902         
44903         var colElementsByName = document.getElementsByName( this.getId() ) ;
44904          
44905         oTextarea.style.display = 'none' ;
44906
44907         if ( oTextarea.tabIndex ) {            
44908             this.TabIndex = oTextarea.tabIndex ;
44909         }
44910         
44911         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
44912         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
44913         this.frame = Roo.get(this.getId() + '___Frame')
44914     },
44915     
44916     _getConfigHtml : function()
44917     {
44918         var sConfig = '' ;
44919
44920         for ( var o in this.fckconfig ) {
44921             sConfig += sConfig.length > 0  ? '&amp;' : '';
44922             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
44923         }
44924
44925         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
44926     },
44927     
44928     
44929     _getIFrameHtml : function()
44930     {
44931         var sFile = 'fckeditor.html' ;
44932         /* no idea what this is about..
44933         try
44934         {
44935             if ( (/fcksource=true/i).test( window.top.location.search ) )
44936                 sFile = 'fckeditor.original.html' ;
44937         }
44938         catch (e) { 
44939         */
44940
44941         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
44942         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
44943         
44944         
44945         var html = '<iframe id="' + this.getId() +
44946             '___Frame" src="' + sLink +
44947             '" width="' + this.width +
44948             '" height="' + this.height + '"' +
44949             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
44950             ' frameborder="0" scrolling="no"></iframe>' ;
44951
44952         return html ;
44953     },
44954     
44955     _insertHtmlBefore : function( html, element )
44956     {
44957         if ( element.insertAdjacentHTML )       {
44958             // IE
44959             element.insertAdjacentHTML( 'beforeBegin', html ) ;
44960         } else { // Gecko
44961             var oRange = document.createRange() ;
44962             oRange.setStartBefore( element ) ;
44963             var oFragment = oRange.createContextualFragment( html );
44964             element.parentNode.insertBefore( oFragment, element ) ;
44965         }
44966     }
44967     
44968     
44969   
44970     
44971     
44972     
44973     
44974
44975 });
44976
44977 //Roo.reg('fckeditor', Roo.form.FCKeditor);
44978
44979 function FCKeditor_OnComplete(editorInstance){
44980     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
44981     f.fckEditor = editorInstance;
44982     //console.log("loaded");
44983     f.fireEvent('editorinit', f, editorInstance);
44984
44985   
44986
44987  
44988
44989
44990
44991
44992
44993
44994
44995
44996
44997
44998
44999
45000
45001
45002
45003 //<script type="text/javascript">
45004 /**
45005  * @class Roo.form.GridField
45006  * @extends Roo.form.Field
45007  * Embed a grid (or editable grid into a form)
45008  * STATUS ALPHA
45009  * 
45010  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
45011  * it needs 
45012  * xgrid.store = Roo.data.Store
45013  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
45014  * xgrid.store.reader = Roo.data.JsonReader 
45015  * 
45016  * 
45017  * @constructor
45018  * Creates a new GridField
45019  * @param {Object} config Configuration options
45020  */
45021 Roo.form.GridField = function(config){
45022     Roo.form.GridField.superclass.constructor.call(this, config);
45023      
45024 };
45025
45026 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
45027     /**
45028      * @cfg {Number} width  - used to restrict width of grid..
45029      */
45030     width : 100,
45031     /**
45032      * @cfg {Number} height - used to restrict height of grid..
45033      */
45034     height : 50,
45035      /**
45036      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
45037          * 
45038          *}
45039      */
45040     xgrid : false, 
45041     /**
45042      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45043      * {tag: "input", type: "checkbox", autocomplete: "off"})
45044      */
45045    // defaultAutoCreate : { tag: 'div' },
45046     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45047     /**
45048      * @cfg {String} addTitle Text to include for adding a title.
45049      */
45050     addTitle : false,
45051     //
45052     onResize : function(){
45053         Roo.form.Field.superclass.onResize.apply(this, arguments);
45054     },
45055
45056     initEvents : function(){
45057         // Roo.form.Checkbox.superclass.initEvents.call(this);
45058         // has no events...
45059        
45060     },
45061
45062
45063     getResizeEl : function(){
45064         return this.wrap;
45065     },
45066
45067     getPositionEl : function(){
45068         return this.wrap;
45069     },
45070
45071     // private
45072     onRender : function(ct, position){
45073         
45074         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
45075         var style = this.style;
45076         delete this.style;
45077         
45078         Roo.form.GridField.superclass.onRender.call(this, ct, position);
45079         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
45080         this.viewEl = this.wrap.createChild({ tag: 'div' });
45081         if (style) {
45082             this.viewEl.applyStyles(style);
45083         }
45084         if (this.width) {
45085             this.viewEl.setWidth(this.width);
45086         }
45087         if (this.height) {
45088             this.viewEl.setHeight(this.height);
45089         }
45090         //if(this.inputValue !== undefined){
45091         //this.setValue(this.value);
45092         
45093         
45094         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
45095         
45096         
45097         this.grid.render();
45098         this.grid.getDataSource().on('remove', this.refreshValue, this);
45099         this.grid.getDataSource().on('update', this.refreshValue, this);
45100         this.grid.on('afteredit', this.refreshValue, this);
45101  
45102     },
45103      
45104     
45105     /**
45106      * Sets the value of the item. 
45107      * @param {String} either an object  or a string..
45108      */
45109     setValue : function(v){
45110         //this.value = v;
45111         v = v || []; // empty set..
45112         // this does not seem smart - it really only affects memoryproxy grids..
45113         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
45114             var ds = this.grid.getDataSource();
45115             // assumes a json reader..
45116             var data = {}
45117             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
45118             ds.loadData( data);
45119         }
45120         // clear selection so it does not get stale.
45121         if (this.grid.sm) { 
45122             this.grid.sm.clearSelections();
45123         }
45124         
45125         Roo.form.GridField.superclass.setValue.call(this, v);
45126         this.refreshValue();
45127         // should load data in the grid really....
45128     },
45129     
45130     // private
45131     refreshValue: function() {
45132          var val = [];
45133         this.grid.getDataSource().each(function(r) {
45134             val.push(r.data);
45135         });
45136         this.el.dom.value = Roo.encode(val);
45137     }
45138     
45139      
45140     
45141     
45142 });/*
45143  * Based on:
45144  * Ext JS Library 1.1.1
45145  * Copyright(c) 2006-2007, Ext JS, LLC.
45146  *
45147  * Originally Released Under LGPL - original licence link has changed is not relivant.
45148  *
45149  * Fork - LGPL
45150  * <script type="text/javascript">
45151  */
45152 /**
45153  * @class Roo.form.DisplayField
45154  * @extends Roo.form.Field
45155  * A generic Field to display non-editable data.
45156  * @constructor
45157  * Creates a new Display Field item.
45158  * @param {Object} config Configuration options
45159  */
45160 Roo.form.DisplayField = function(config){
45161     Roo.form.DisplayField.superclass.constructor.call(this, config);
45162     
45163 };
45164
45165 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45166     inputType:      'hidden',
45167     allowBlank:     true,
45168     readOnly:         true,
45169     
45170  
45171     /**
45172      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45173      */
45174     focusClass : undefined,
45175     /**
45176      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45177      */
45178     fieldClass: 'x-form-field',
45179     
45180      /**
45181      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45182      */
45183     valueRenderer: undefined,
45184     
45185     width: 100,
45186     /**
45187      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45188      * {tag: "input", type: "checkbox", autocomplete: "off"})
45189      */
45190      
45191  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45192
45193     onResize : function(){
45194         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45195         
45196     },
45197
45198     initEvents : function(){
45199         // Roo.form.Checkbox.superclass.initEvents.call(this);
45200         // has no events...
45201        
45202     },
45203
45204
45205     getResizeEl : function(){
45206         return this.wrap;
45207     },
45208
45209     getPositionEl : function(){
45210         return this.wrap;
45211     },
45212
45213     // private
45214     onRender : function(ct, position){
45215         
45216         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
45217         //if(this.inputValue !== undefined){
45218         this.wrap = this.el.wrap();
45219         
45220         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
45221         
45222         if (this.bodyStyle) {
45223             this.viewEl.applyStyles(this.bodyStyle);
45224         }
45225         //this.viewEl.setStyle('padding', '2px');
45226         
45227         this.setValue(this.value);
45228         
45229     },
45230 /*
45231     // private
45232     initValue : Roo.emptyFn,
45233
45234   */
45235
45236         // private
45237     onClick : function(){
45238         
45239     },
45240
45241     /**
45242      * Sets the checked state of the checkbox.
45243      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
45244      */
45245     setValue : function(v){
45246         this.value = v;
45247         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
45248         // this might be called before we have a dom element..
45249         if (!this.viewEl) {
45250             return;
45251         }
45252         this.viewEl.dom.innerHTML = html;
45253         Roo.form.DisplayField.superclass.setValue.call(this, v);
45254
45255     }
45256 });/*
45257  * 
45258  * Licence- LGPL
45259  * 
45260  */
45261
45262 /**
45263  * @class Roo.form.DayPicker
45264  * @extends Roo.form.Field
45265  * A Day picker show [M] [T] [W] ....
45266  * @constructor
45267  * Creates a new Day Picker
45268  * @param {Object} config Configuration options
45269  */
45270 Roo.form.DayPicker= function(config){
45271     Roo.form.DayPicker.superclass.constructor.call(this, config);
45272      
45273 };
45274
45275 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
45276     /**
45277      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45278      */
45279     focusClass : undefined,
45280     /**
45281      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45282      */
45283     fieldClass: "x-form-field",
45284    
45285     /**
45286      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45287      * {tag: "input", type: "checkbox", autocomplete: "off"})
45288      */
45289     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
45290     
45291    
45292     actionMode : 'viewEl', 
45293     //
45294     // private
45295  
45296     inputType : 'hidden',
45297     
45298      
45299     inputElement: false, // real input element?
45300     basedOn: false, // ????
45301     
45302     isFormField: true, // not sure where this is needed!!!!
45303
45304     onResize : function(){
45305         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
45306         if(!this.boxLabel){
45307             this.el.alignTo(this.wrap, 'c-c');
45308         }
45309     },
45310
45311     initEvents : function(){
45312         Roo.form.Checkbox.superclass.initEvents.call(this);
45313         this.el.on("click", this.onClick,  this);
45314         this.el.on("change", this.onClick,  this);
45315     },
45316
45317
45318     getResizeEl : function(){
45319         return this.wrap;
45320     },
45321
45322     getPositionEl : function(){
45323         return this.wrap;
45324     },
45325
45326     
45327     // private
45328     onRender : function(ct, position){
45329         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45330        
45331         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
45332         
45333         var r1 = '<table><tr>';
45334         var r2 = '<tr class="x-form-daypick-icons">';
45335         for (var i=0; i < 7; i++) {
45336             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
45337             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
45338         }
45339         
45340         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
45341         viewEl.select('img').on('click', this.onClick, this);
45342         this.viewEl = viewEl;   
45343         
45344         
45345         // this will not work on Chrome!!!
45346         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
45347         this.el.on('propertychange', this.setFromHidden,  this);  //ie
45348         
45349         
45350           
45351
45352     },
45353
45354     // private
45355     initValue : Roo.emptyFn,
45356
45357     /**
45358      * Returns the checked state of the checkbox.
45359      * @return {Boolean} True if checked, else false
45360      */
45361     getValue : function(){
45362         return this.el.dom.value;
45363         
45364     },
45365
45366         // private
45367     onClick : function(e){ 
45368         //this.setChecked(!this.checked);
45369         Roo.get(e.target).toggleClass('x-menu-item-checked');
45370         this.refreshValue();
45371         //if(this.el.dom.checked != this.checked){
45372         //    this.setValue(this.el.dom.checked);
45373        // }
45374     },
45375     
45376     // private
45377     refreshValue : function()
45378     {
45379         var val = '';
45380         this.viewEl.select('img',true).each(function(e,i,n)  {
45381             val += e.is(".x-menu-item-checked") ? String(n) : '';
45382         });
45383         this.setValue(val, true);
45384     },
45385
45386     /**
45387      * Sets the checked state of the checkbox.
45388      * On is always based on a string comparison between inputValue and the param.
45389      * @param {Boolean/String} value - the value to set 
45390      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
45391      */
45392     setValue : function(v,suppressEvent){
45393         if (!this.el.dom) {
45394             return;
45395         }
45396         var old = this.el.dom.value ;
45397         this.el.dom.value = v;
45398         if (suppressEvent) {
45399             return ;
45400         }
45401          
45402         // update display..
45403         this.viewEl.select('img',true).each(function(e,i,n)  {
45404             
45405             var on = e.is(".x-menu-item-checked");
45406             var newv = v.indexOf(String(n)) > -1;
45407             if (on != newv) {
45408                 e.toggleClass('x-menu-item-checked');
45409             }
45410             
45411         });
45412         
45413         
45414         this.fireEvent('change', this, v, old);
45415         
45416         
45417     },
45418    
45419     // handle setting of hidden value by some other method!!?!?
45420     setFromHidden: function()
45421     {
45422         if(!this.el){
45423             return;
45424         }
45425         //console.log("SET FROM HIDDEN");
45426         //alert('setFrom hidden');
45427         this.setValue(this.el.dom.value);
45428     },
45429     
45430     onDestroy : function()
45431     {
45432         if(this.viewEl){
45433             Roo.get(this.viewEl).remove();
45434         }
45435          
45436         Roo.form.DayPicker.superclass.onDestroy.call(this);
45437     }
45438
45439 });/*
45440  * RooJS Library 1.1.1
45441  * Copyright(c) 2008-2011  Alan Knowles
45442  *
45443  * License - LGPL
45444  */
45445  
45446
45447 /**
45448  * @class Roo.form.ComboCheck
45449  * @extends Roo.form.ComboBox
45450  * A combobox for multiple select items.
45451  *
45452  * FIXME - could do with a reset button..
45453  * 
45454  * @constructor
45455  * Create a new ComboCheck
45456  * @param {Object} config Configuration options
45457  */
45458 Roo.form.ComboCheck = function(config){
45459     Roo.form.ComboCheck.superclass.constructor.call(this, config);
45460     // should verify some data...
45461     // like
45462     // hiddenName = required..
45463     // displayField = required
45464     // valudField == required
45465     var req= [ 'hiddenName', 'displayField', 'valueField' ];
45466     var _t = this;
45467     Roo.each(req, function(e) {
45468         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
45469             throw "Roo.form.ComboCheck : missing value for: " + e;
45470         }
45471     });
45472     
45473     
45474 };
45475
45476 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
45477      
45478      
45479     editable : false,
45480      
45481     selectedClass: 'x-menu-item-checked', 
45482     
45483     // private
45484     onRender : function(ct, position){
45485         var _t = this;
45486         
45487         
45488         
45489         if(!this.tpl){
45490             var cls = 'x-combo-list';
45491
45492             
45493             this.tpl =  new Roo.Template({
45494                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
45495                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
45496                    '<span>{' + this.displayField + '}</span>' +
45497                     '</div>' 
45498                 
45499             });
45500         }
45501  
45502         
45503         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
45504         this.view.singleSelect = false;
45505         this.view.multiSelect = true;
45506         this.view.toggleSelect = true;
45507         this.pageTb.add(new Roo.Toolbar.Fill(), {
45508             
45509             text: 'Done',
45510             handler: function()
45511             {
45512                 _t.collapse();
45513             }
45514         });
45515     },
45516     
45517     onViewOver : function(e, t){
45518         // do nothing...
45519         return;
45520         
45521     },
45522     
45523     onViewClick : function(doFocus,index){
45524         return;
45525         
45526     },
45527     select: function () {
45528         //Roo.log("SELECT CALLED");
45529     },
45530      
45531     selectByValue : function(xv, scrollIntoView){
45532         var ar = this.getValueArray();
45533         var sels = [];
45534         
45535         Roo.each(ar, function(v) {
45536             if(v === undefined || v === null){
45537                 return;
45538             }
45539             var r = this.findRecord(this.valueField, v);
45540             if(r){
45541                 sels.push(this.store.indexOf(r))
45542                 
45543             }
45544         },this);
45545         this.view.select(sels);
45546         return false;
45547     },
45548     
45549     
45550     
45551     onSelect : function(record, index){
45552        // Roo.log("onselect Called");
45553        // this is only called by the clear button now..
45554         this.view.clearSelections();
45555         this.setValue('[]');
45556         if (this.value != this.valueBefore) {
45557             this.fireEvent('change', this, this.value, this.valueBefore);
45558             this.valueBefore = this.value;
45559         }
45560     },
45561     getValueArray : function()
45562     {
45563         var ar = [] ;
45564         
45565         try {
45566             //Roo.log(this.value);
45567             if (typeof(this.value) == 'undefined') {
45568                 return [];
45569             }
45570             var ar = Roo.decode(this.value);
45571             return  ar instanceof Array ? ar : []; //?? valid?
45572             
45573         } catch(e) {
45574             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
45575             return [];
45576         }
45577          
45578     },
45579     expand : function ()
45580     {
45581         
45582         Roo.form.ComboCheck.superclass.expand.call(this);
45583         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
45584         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
45585         
45586
45587     },
45588     
45589     collapse : function(){
45590         Roo.form.ComboCheck.superclass.collapse.call(this);
45591         var sl = this.view.getSelectedIndexes();
45592         var st = this.store;
45593         var nv = [];
45594         var tv = [];
45595         var r;
45596         Roo.each(sl, function(i) {
45597             r = st.getAt(i);
45598             nv.push(r.get(this.valueField));
45599         },this);
45600         this.setValue(Roo.encode(nv));
45601         if (this.value != this.valueBefore) {
45602
45603             this.fireEvent('change', this, this.value, this.valueBefore);
45604             this.valueBefore = this.value;
45605         }
45606         
45607     },
45608     
45609     setValue : function(v){
45610         // Roo.log(v);
45611         this.value = v;
45612         
45613         var vals = this.getValueArray();
45614         var tv = [];
45615         Roo.each(vals, function(k) {
45616             var r = this.findRecord(this.valueField, k);
45617             if(r){
45618                 tv.push(r.data[this.displayField]);
45619             }else if(this.valueNotFoundText !== undefined){
45620                 tv.push( this.valueNotFoundText );
45621             }
45622         },this);
45623        // Roo.log(tv);
45624         
45625         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
45626         this.hiddenField.value = v;
45627         this.value = v;
45628     }
45629     
45630 });//<script type="text/javasscript">
45631  
45632
45633 /**
45634  * @class Roo.DDView
45635  * A DnD enabled version of Roo.View.
45636  * @param {Element/String} container The Element in which to create the View.
45637  * @param {String} tpl The template string used to create the markup for each element of the View
45638  * @param {Object} config The configuration properties. These include all the config options of
45639  * {@link Roo.View} plus some specific to this class.<br>
45640  * <p>
45641  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
45642  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
45643  * <p>
45644  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
45645 .x-view-drag-insert-above {
45646         border-top:1px dotted #3366cc;
45647 }
45648 .x-view-drag-insert-below {
45649         border-bottom:1px dotted #3366cc;
45650 }
45651 </code></pre>
45652  * 
45653  */
45654  
45655 Roo.DDView = function(container, tpl, config) {
45656     Roo.DDView.superclass.constructor.apply(this, arguments);
45657     this.getEl().setStyle("outline", "0px none");
45658     this.getEl().unselectable();
45659     if (this.dragGroup) {
45660                 this.setDraggable(this.dragGroup.split(","));
45661     }
45662     if (this.dropGroup) {
45663                 this.setDroppable(this.dropGroup.split(","));
45664     }
45665     if (this.deletable) {
45666         this.setDeletable();
45667     }
45668     this.isDirtyFlag = false;
45669         this.addEvents({
45670                 "drop" : true
45671         });
45672 };
45673
45674 Roo.extend(Roo.DDView, Roo.View, {
45675 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
45676 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
45677 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
45678 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
45679
45680         isFormField: true,
45681
45682         reset: Roo.emptyFn,
45683         
45684         clearInvalid: Roo.form.Field.prototype.clearInvalid,
45685
45686         validate: function() {
45687                 return true;
45688         },
45689         
45690         destroy: function() {
45691                 this.purgeListeners();
45692                 this.getEl.removeAllListeners();
45693                 this.getEl().remove();
45694                 if (this.dragZone) {
45695                         if (this.dragZone.destroy) {
45696                                 this.dragZone.destroy();
45697                         }
45698                 }
45699                 if (this.dropZone) {
45700                         if (this.dropZone.destroy) {
45701                                 this.dropZone.destroy();
45702                         }
45703                 }
45704         },
45705
45706 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
45707         getName: function() {
45708                 return this.name;
45709         },
45710
45711 /**     Loads the View from a JSON string representing the Records to put into the Store. */
45712         setValue: function(v) {
45713                 if (!this.store) {
45714                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
45715                 }
45716                 var data = {};
45717                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
45718                 this.store.proxy = new Roo.data.MemoryProxy(data);
45719                 this.store.load();
45720         },
45721
45722 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
45723         getValue: function() {
45724                 var result = '(';
45725                 this.store.each(function(rec) {
45726                         result += rec.id + ',';
45727                 });
45728                 return result.substr(0, result.length - 1) + ')';
45729         },
45730         
45731         getIds: function() {
45732                 var i = 0, result = new Array(this.store.getCount());
45733                 this.store.each(function(rec) {
45734                         result[i++] = rec.id;
45735                 });
45736                 return result;
45737         },
45738         
45739         isDirty: function() {
45740                 return this.isDirtyFlag;
45741         },
45742
45743 /**
45744  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
45745  *      whole Element becomes the target, and this causes the drop gesture to append.
45746  */
45747     getTargetFromEvent : function(e) {
45748                 var target = e.getTarget();
45749                 while ((target !== null) && (target.parentNode != this.el.dom)) {
45750                 target = target.parentNode;
45751                 }
45752                 if (!target) {
45753                         target = this.el.dom.lastChild || this.el.dom;
45754                 }
45755                 return target;
45756     },
45757
45758 /**
45759  *      Create the drag data which consists of an object which has the property "ddel" as
45760  *      the drag proxy element. 
45761  */
45762     getDragData : function(e) {
45763         var target = this.findItemFromChild(e.getTarget());
45764                 if(target) {
45765                         this.handleSelection(e);
45766                         var selNodes = this.getSelectedNodes();
45767             var dragData = {
45768                 source: this,
45769                 copy: this.copy || (this.allowCopy && e.ctrlKey),
45770                 nodes: selNodes,
45771                 records: []
45772                         };
45773                         var selectedIndices = this.getSelectedIndexes();
45774                         for (var i = 0; i < selectedIndices.length; i++) {
45775                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
45776                         }
45777                         if (selNodes.length == 1) {
45778                                 dragData.ddel = target.cloneNode(true); // the div element
45779                         } else {
45780                                 var div = document.createElement('div'); // create the multi element drag "ghost"
45781                                 div.className = 'multi-proxy';
45782                                 for (var i = 0, len = selNodes.length; i < len; i++) {
45783                                         div.appendChild(selNodes[i].cloneNode(true));
45784                                 }
45785                                 dragData.ddel = div;
45786                         }
45787             //console.log(dragData)
45788             //console.log(dragData.ddel.innerHTML)
45789                         return dragData;
45790                 }
45791         //console.log('nodragData')
45792                 return false;
45793     },
45794     
45795 /**     Specify to which ddGroup items in this DDView may be dragged. */
45796     setDraggable: function(ddGroup) {
45797         if (ddGroup instanceof Array) {
45798                 Roo.each(ddGroup, this.setDraggable, this);
45799                 return;
45800         }
45801         if (this.dragZone) {
45802                 this.dragZone.addToGroup(ddGroup);
45803         } else {
45804                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
45805                                 containerScroll: true,
45806                                 ddGroup: ddGroup 
45807
45808                         });
45809 //                      Draggability implies selection. DragZone's mousedown selects the element.
45810                         if (!this.multiSelect) { this.singleSelect = true; }
45811
45812 //                      Wire the DragZone's handlers up to methods in *this*
45813                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
45814                 }
45815     },
45816
45817 /**     Specify from which ddGroup this DDView accepts drops. */
45818     setDroppable: function(ddGroup) {
45819         if (ddGroup instanceof Array) {
45820                 Roo.each(ddGroup, this.setDroppable, this);
45821                 return;
45822         }
45823         if (this.dropZone) {
45824                 this.dropZone.addToGroup(ddGroup);
45825         } else {
45826                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
45827                                 containerScroll: true,
45828                                 ddGroup: ddGroup
45829                         });
45830
45831 //                      Wire the DropZone's handlers up to methods in *this*
45832                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
45833                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
45834                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
45835                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
45836                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
45837                 }
45838     },
45839
45840 /**     Decide whether to drop above or below a View node. */
45841     getDropPoint : function(e, n, dd){
45842         if (n == this.el.dom) { return "above"; }
45843                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
45844                 var c = t + (b - t) / 2;
45845                 var y = Roo.lib.Event.getPageY(e);
45846                 if(y <= c) {
45847                         return "above";
45848                 }else{
45849                         return "below";
45850                 }
45851     },
45852
45853     onNodeEnter : function(n, dd, e, data){
45854                 return false;
45855     },
45856     
45857     onNodeOver : function(n, dd, e, data){
45858                 var pt = this.getDropPoint(e, n, dd);
45859                 // set the insert point style on the target node
45860                 var dragElClass = this.dropNotAllowed;
45861                 if (pt) {
45862                         var targetElClass;
45863                         if (pt == "above"){
45864                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
45865                                 targetElClass = "x-view-drag-insert-above";
45866                         } else {
45867                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
45868                                 targetElClass = "x-view-drag-insert-below";
45869                         }
45870                         if (this.lastInsertClass != targetElClass){
45871                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
45872                                 this.lastInsertClass = targetElClass;
45873                         }
45874                 }
45875                 return dragElClass;
45876         },
45877
45878     onNodeOut : function(n, dd, e, data){
45879                 this.removeDropIndicators(n);
45880     },
45881
45882     onNodeDrop : function(n, dd, e, data){
45883         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
45884                 return false;
45885         }
45886         var pt = this.getDropPoint(e, n, dd);
45887                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
45888                 if (pt == "below") { insertAt++; }
45889                 for (var i = 0; i < data.records.length; i++) {
45890                         var r = data.records[i];
45891                         var dup = this.store.getById(r.id);
45892                         if (dup && (dd != this.dragZone)) {
45893                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
45894                         } else {
45895                                 if (data.copy) {
45896                                         this.store.insert(insertAt++, r.copy());
45897                                 } else {
45898                                         data.source.isDirtyFlag = true;
45899                                         r.store.remove(r);
45900                                         this.store.insert(insertAt++, r);
45901                                 }
45902                                 this.isDirtyFlag = true;
45903                         }
45904                 }
45905                 this.dragZone.cachedTarget = null;
45906                 return true;
45907     },
45908
45909     removeDropIndicators : function(n){
45910                 if(n){
45911                         Roo.fly(n).removeClass([
45912                                 "x-view-drag-insert-above",
45913                                 "x-view-drag-insert-below"]);
45914                         this.lastInsertClass = "_noclass";
45915                 }
45916     },
45917
45918 /**
45919  *      Utility method. Add a delete option to the DDView's context menu.
45920  *      @param {String} imageUrl The URL of the "delete" icon image.
45921  */
45922         setDeletable: function(imageUrl) {
45923                 if (!this.singleSelect && !this.multiSelect) {
45924                         this.singleSelect = true;
45925                 }
45926                 var c = this.getContextMenu();
45927                 this.contextMenu.on("itemclick", function(item) {
45928                         switch (item.id) {
45929                                 case "delete":
45930                                         this.remove(this.getSelectedIndexes());
45931                                         break;
45932                         }
45933                 }, this);
45934                 this.contextMenu.add({
45935                         icon: imageUrl,
45936                         id: "delete",
45937                         text: 'Delete'
45938                 });
45939         },
45940         
45941 /**     Return the context menu for this DDView. */
45942         getContextMenu: function() {
45943                 if (!this.contextMenu) {
45944 //                      Create the View's context menu
45945                         this.contextMenu = new Roo.menu.Menu({
45946                                 id: this.id + "-contextmenu"
45947                         });
45948                         this.el.on("contextmenu", this.showContextMenu, this);
45949                 }
45950                 return this.contextMenu;
45951         },
45952         
45953         disableContextMenu: function() {
45954                 if (this.contextMenu) {
45955                         this.el.un("contextmenu", this.showContextMenu, this);
45956                 }
45957         },
45958
45959         showContextMenu: function(e, item) {
45960         item = this.findItemFromChild(e.getTarget());
45961                 if (item) {
45962                         e.stopEvent();
45963                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
45964                         this.contextMenu.showAt(e.getXY());
45965             }
45966     },
45967
45968 /**
45969  *      Remove {@link Roo.data.Record}s at the specified indices.
45970  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
45971  */
45972     remove: function(selectedIndices) {
45973                 selectedIndices = [].concat(selectedIndices);
45974                 for (var i = 0; i < selectedIndices.length; i++) {
45975                         var rec = this.store.getAt(selectedIndices[i]);
45976                         this.store.remove(rec);
45977                 }
45978     },
45979
45980 /**
45981  *      Double click fires the event, but also, if this is draggable, and there is only one other
45982  *      related DropZone, it transfers the selected node.
45983  */
45984     onDblClick : function(e){
45985         var item = this.findItemFromChild(e.getTarget());
45986         if(item){
45987             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
45988                 return false;
45989             }
45990             if (this.dragGroup) {
45991                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
45992                     while (targets.indexOf(this.dropZone) > -1) {
45993                             targets.remove(this.dropZone);
45994                                 }
45995                     if (targets.length == 1) {
45996                                         this.dragZone.cachedTarget = null;
45997                         var el = Roo.get(targets[0].getEl());
45998                         var box = el.getBox(true);
45999                         targets[0].onNodeDrop(el.dom, {
46000                                 target: el.dom,
46001                                 xy: [box.x, box.y + box.height - 1]
46002                         }, null, this.getDragData(e));
46003                     }
46004                 }
46005         }
46006     },
46007     
46008     handleSelection: function(e) {
46009                 this.dragZone.cachedTarget = null;
46010         var item = this.findItemFromChild(e.getTarget());
46011         if (!item) {
46012                 this.clearSelections(true);
46013                 return;
46014         }
46015                 if (item && (this.multiSelect || this.singleSelect)){
46016                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
46017                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
46018                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
46019                                 this.unselect(item);
46020                         } else {
46021                                 this.select(item, this.multiSelect && e.ctrlKey);
46022                                 this.lastSelection = item;
46023                         }
46024                 }
46025     },
46026
46027     onItemClick : function(item, index, e){
46028                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
46029                         return false;
46030                 }
46031                 return true;
46032     },
46033
46034     unselect : function(nodeInfo, suppressEvent){
46035                 var node = this.getNode(nodeInfo);
46036                 if(node && this.isSelected(node)){
46037                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
46038                                 Roo.fly(node).removeClass(this.selectedClass);
46039                                 this.selections.remove(node);
46040                                 if(!suppressEvent){
46041                                         this.fireEvent("selectionchange", this, this.selections);
46042                                 }
46043                         }
46044                 }
46045     }
46046 });
46047 /*
46048  * Based on:
46049  * Ext JS Library 1.1.1
46050  * Copyright(c) 2006-2007, Ext JS, LLC.
46051  *
46052  * Originally Released Under LGPL - original licence link has changed is not relivant.
46053  *
46054  * Fork - LGPL
46055  * <script type="text/javascript">
46056  */
46057  
46058 /**
46059  * @class Roo.LayoutManager
46060  * @extends Roo.util.Observable
46061  * Base class for layout managers.
46062  */
46063 Roo.LayoutManager = function(container, config){
46064     Roo.LayoutManager.superclass.constructor.call(this);
46065     this.el = Roo.get(container);
46066     // ie scrollbar fix
46067     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
46068         document.body.scroll = "no";
46069     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
46070         this.el.position('relative');
46071     }
46072     this.id = this.el.id;
46073     this.el.addClass("x-layout-container");
46074     /** false to disable window resize monitoring @type Boolean */
46075     this.monitorWindowResize = true;
46076     this.regions = {};
46077     this.addEvents({
46078         /**
46079          * @event layout
46080          * Fires when a layout is performed. 
46081          * @param {Roo.LayoutManager} this
46082          */
46083         "layout" : true,
46084         /**
46085          * @event regionresized
46086          * Fires when the user resizes a region. 
46087          * @param {Roo.LayoutRegion} region The resized region
46088          * @param {Number} newSize The new size (width for east/west, height for north/south)
46089          */
46090         "regionresized" : true,
46091         /**
46092          * @event regioncollapsed
46093          * Fires when a region is collapsed. 
46094          * @param {Roo.LayoutRegion} region The collapsed region
46095          */
46096         "regioncollapsed" : true,
46097         /**
46098          * @event regionexpanded
46099          * Fires when a region is expanded.  
46100          * @param {Roo.LayoutRegion} region The expanded region
46101          */
46102         "regionexpanded" : true
46103     });
46104     this.updating = false;
46105     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
46106 };
46107
46108 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
46109     /**
46110      * Returns true if this layout is currently being updated
46111      * @return {Boolean}
46112      */
46113     isUpdating : function(){
46114         return this.updating; 
46115     },
46116     
46117     /**
46118      * Suspend the LayoutManager from doing auto-layouts while
46119      * making multiple add or remove calls
46120      */
46121     beginUpdate : function(){
46122         this.updating = true;    
46123     },
46124     
46125     /**
46126      * Restore auto-layouts and optionally disable the manager from performing a layout
46127      * @param {Boolean} noLayout true to disable a layout update 
46128      */
46129     endUpdate : function(noLayout){
46130         this.updating = false;
46131         if(!noLayout){
46132             this.layout();
46133         }    
46134     },
46135     
46136     layout: function(){
46137         
46138     },
46139     
46140     onRegionResized : function(region, newSize){
46141         this.fireEvent("regionresized", region, newSize);
46142         this.layout();
46143     },
46144     
46145     onRegionCollapsed : function(region){
46146         this.fireEvent("regioncollapsed", region);
46147     },
46148     
46149     onRegionExpanded : function(region){
46150         this.fireEvent("regionexpanded", region);
46151     },
46152         
46153     /**
46154      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
46155      * performs box-model adjustments.
46156      * @return {Object} The size as an object {width: (the width), height: (the height)}
46157      */
46158     getViewSize : function(){
46159         var size;
46160         if(this.el.dom != document.body){
46161             size = this.el.getSize();
46162         }else{
46163             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
46164         }
46165         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
46166         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46167         return size;
46168     },
46169     
46170     /**
46171      * Returns the Element this layout is bound to.
46172      * @return {Roo.Element}
46173      */
46174     getEl : function(){
46175         return this.el;
46176     },
46177     
46178     /**
46179      * Returns the specified region.
46180      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
46181      * @return {Roo.LayoutRegion}
46182      */
46183     getRegion : function(target){
46184         return this.regions[target.toLowerCase()];
46185     },
46186     
46187     onWindowResize : function(){
46188         if(this.monitorWindowResize){
46189             this.layout();
46190         }
46191     }
46192 });/*
46193  * Based on:
46194  * Ext JS Library 1.1.1
46195  * Copyright(c) 2006-2007, Ext JS, LLC.
46196  *
46197  * Originally Released Under LGPL - original licence link has changed is not relivant.
46198  *
46199  * Fork - LGPL
46200  * <script type="text/javascript">
46201  */
46202 /**
46203  * @class Roo.BorderLayout
46204  * @extends Roo.LayoutManager
46205  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
46206  * please see: <br><br>
46207  * <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>
46208  * <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>
46209  * Example:
46210  <pre><code>
46211  var layout = new Roo.BorderLayout(document.body, {
46212     north: {
46213         initialSize: 25,
46214         titlebar: false
46215     },
46216     west: {
46217         split:true,
46218         initialSize: 200,
46219         minSize: 175,
46220         maxSize: 400,
46221         titlebar: true,
46222         collapsible: true
46223     },
46224     east: {
46225         split:true,
46226         initialSize: 202,
46227         minSize: 175,
46228         maxSize: 400,
46229         titlebar: true,
46230         collapsible: true
46231     },
46232     south: {
46233         split:true,
46234         initialSize: 100,
46235         minSize: 100,
46236         maxSize: 200,
46237         titlebar: true,
46238         collapsible: true
46239     },
46240     center: {
46241         titlebar: true,
46242         autoScroll:true,
46243         resizeTabs: true,
46244         minTabWidth: 50,
46245         preferredTabWidth: 150
46246     }
46247 });
46248
46249 // shorthand
46250 var CP = Roo.ContentPanel;
46251
46252 layout.beginUpdate();
46253 layout.add("north", new CP("north", "North"));
46254 layout.add("south", new CP("south", {title: "South", closable: true}));
46255 layout.add("west", new CP("west", {title: "West"}));
46256 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
46257 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
46258 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
46259 layout.getRegion("center").showPanel("center1");
46260 layout.endUpdate();
46261 </code></pre>
46262
46263 <b>The container the layout is rendered into can be either the body element or any other element.
46264 If it is not the body element, the container needs to either be an absolute positioned element,
46265 or you will need to add "position:relative" to the css of the container.  You will also need to specify
46266 the container size if it is not the body element.</b>
46267
46268 * @constructor
46269 * Create a new BorderLayout
46270 * @param {String/HTMLElement/Element} container The container this layout is bound to
46271 * @param {Object} config Configuration options
46272  */
46273 Roo.BorderLayout = function(container, config){
46274     config = config || {};
46275     Roo.BorderLayout.superclass.constructor.call(this, container, config);
46276     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
46277     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
46278         var target = this.factory.validRegions[i];
46279         if(config[target]){
46280             this.addRegion(target, config[target]);
46281         }
46282     }
46283 };
46284
46285 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
46286     /**
46287      * Creates and adds a new region if it doesn't already exist.
46288      * @param {String} target The target region key (north, south, east, west or center).
46289      * @param {Object} config The regions config object
46290      * @return {BorderLayoutRegion} The new region
46291      */
46292     addRegion : function(target, config){
46293         if(!this.regions[target]){
46294             var r = this.factory.create(target, this, config);
46295             this.bindRegion(target, r);
46296         }
46297         return this.regions[target];
46298     },
46299
46300     // private (kinda)
46301     bindRegion : function(name, r){
46302         this.regions[name] = r;
46303         r.on("visibilitychange", this.layout, this);
46304         r.on("paneladded", this.layout, this);
46305         r.on("panelremoved", this.layout, this);
46306         r.on("invalidated", this.layout, this);
46307         r.on("resized", this.onRegionResized, this);
46308         r.on("collapsed", this.onRegionCollapsed, this);
46309         r.on("expanded", this.onRegionExpanded, this);
46310     },
46311
46312     /**
46313      * Performs a layout update.
46314      */
46315     layout : function(){
46316         if(this.updating) return;
46317         var size = this.getViewSize();
46318         var w = size.width;
46319         var h = size.height;
46320         var centerW = w;
46321         var centerH = h;
46322         var centerY = 0;
46323         var centerX = 0;
46324         //var x = 0, y = 0;
46325
46326         var rs = this.regions;
46327         var north = rs["north"];
46328         var south = rs["south"]; 
46329         var west = rs["west"];
46330         var east = rs["east"];
46331         var center = rs["center"];
46332         //if(this.hideOnLayout){ // not supported anymore
46333             //c.el.setStyle("display", "none");
46334         //}
46335         if(north && north.isVisible()){
46336             var b = north.getBox();
46337             var m = north.getMargins();
46338             b.width = w - (m.left+m.right);
46339             b.x = m.left;
46340             b.y = m.top;
46341             centerY = b.height + b.y + m.bottom;
46342             centerH -= centerY;
46343             north.updateBox(this.safeBox(b));
46344         }
46345         if(south && south.isVisible()){
46346             var b = south.getBox();
46347             var m = south.getMargins();
46348             b.width = w - (m.left+m.right);
46349             b.x = m.left;
46350             var totalHeight = (b.height + m.top + m.bottom);
46351             b.y = h - totalHeight + m.top;
46352             centerH -= totalHeight;
46353             south.updateBox(this.safeBox(b));
46354         }
46355         if(west && west.isVisible()){
46356             var b = west.getBox();
46357             var m = west.getMargins();
46358             b.height = centerH - (m.top+m.bottom);
46359             b.x = m.left;
46360             b.y = centerY + m.top;
46361             var totalWidth = (b.width + m.left + m.right);
46362             centerX += totalWidth;
46363             centerW -= totalWidth;
46364             west.updateBox(this.safeBox(b));
46365         }
46366         if(east && east.isVisible()){
46367             var b = east.getBox();
46368             var m = east.getMargins();
46369             b.height = centerH - (m.top+m.bottom);
46370             var totalWidth = (b.width + m.left + m.right);
46371             b.x = w - totalWidth + m.left;
46372             b.y = centerY + m.top;
46373             centerW -= totalWidth;
46374             east.updateBox(this.safeBox(b));
46375         }
46376         if(center){
46377             var m = center.getMargins();
46378             var centerBox = {
46379                 x: centerX + m.left,
46380                 y: centerY + m.top,
46381                 width: centerW - (m.left+m.right),
46382                 height: centerH - (m.top+m.bottom)
46383             };
46384             //if(this.hideOnLayout){
46385                 //center.el.setStyle("display", "block");
46386             //}
46387             center.updateBox(this.safeBox(centerBox));
46388         }
46389         this.el.repaint();
46390         this.fireEvent("layout", this);
46391     },
46392
46393     // private
46394     safeBox : function(box){
46395         box.width = Math.max(0, box.width);
46396         box.height = Math.max(0, box.height);
46397         return box;
46398     },
46399
46400     /**
46401      * Adds a ContentPanel (or subclass) to this layout.
46402      * @param {String} target The target region key (north, south, east, west or center).
46403      * @param {Roo.ContentPanel} panel The panel to add
46404      * @return {Roo.ContentPanel} The added panel
46405      */
46406     add : function(target, panel){
46407          
46408         target = target.toLowerCase();
46409         return this.regions[target].add(panel);
46410     },
46411
46412     /**
46413      * Remove a ContentPanel (or subclass) to this layout.
46414      * @param {String} target The target region key (north, south, east, west or center).
46415      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
46416      * @return {Roo.ContentPanel} The removed panel
46417      */
46418     remove : function(target, panel){
46419         target = target.toLowerCase();
46420         return this.regions[target].remove(panel);
46421     },
46422
46423     /**
46424      * Searches all regions for a panel with the specified id
46425      * @param {String} panelId
46426      * @return {Roo.ContentPanel} The panel or null if it wasn't found
46427      */
46428     findPanel : function(panelId){
46429         var rs = this.regions;
46430         for(var target in rs){
46431             if(typeof rs[target] != "function"){
46432                 var p = rs[target].getPanel(panelId);
46433                 if(p){
46434                     return p;
46435                 }
46436             }
46437         }
46438         return null;
46439     },
46440
46441     /**
46442      * Searches all regions for a panel with the specified id and activates (shows) it.
46443      * @param {String/ContentPanel} panelId The panels id or the panel itself
46444      * @return {Roo.ContentPanel} The shown panel or null
46445      */
46446     showPanel : function(panelId) {
46447       var rs = this.regions;
46448       for(var target in rs){
46449          var r = rs[target];
46450          if(typeof r != "function"){
46451             if(r.hasPanel(panelId)){
46452                return r.showPanel(panelId);
46453             }
46454          }
46455       }
46456       return null;
46457    },
46458
46459    /**
46460      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
46461      * @param {Roo.state.Provider} provider (optional) An alternate state provider
46462      */
46463     restoreState : function(provider){
46464         if(!provider){
46465             provider = Roo.state.Manager;
46466         }
46467         var sm = new Roo.LayoutStateManager();
46468         sm.init(this, provider);
46469     },
46470
46471     /**
46472      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
46473      * object should contain properties for each region to add ContentPanels to, and each property's value should be
46474      * a valid ContentPanel config object.  Example:
46475      * <pre><code>
46476 // Create the main layout
46477 var layout = new Roo.BorderLayout('main-ct', {
46478     west: {
46479         split:true,
46480         minSize: 175,
46481         titlebar: true
46482     },
46483     center: {
46484         title:'Components'
46485     }
46486 }, 'main-ct');
46487
46488 // Create and add multiple ContentPanels at once via configs
46489 layout.batchAdd({
46490    west: {
46491        id: 'source-files',
46492        autoCreate:true,
46493        title:'Ext Source Files',
46494        autoScroll:true,
46495        fitToFrame:true
46496    },
46497    center : {
46498        el: cview,
46499        autoScroll:true,
46500        fitToFrame:true,
46501        toolbar: tb,
46502        resizeEl:'cbody'
46503    }
46504 });
46505 </code></pre>
46506      * @param {Object} regions An object containing ContentPanel configs by region name
46507      */
46508     batchAdd : function(regions){
46509         this.beginUpdate();
46510         for(var rname in regions){
46511             var lr = this.regions[rname];
46512             if(lr){
46513                 this.addTypedPanels(lr, regions[rname]);
46514             }
46515         }
46516         this.endUpdate();
46517     },
46518
46519     // private
46520     addTypedPanels : function(lr, ps){
46521         if(typeof ps == 'string'){
46522             lr.add(new Roo.ContentPanel(ps));
46523         }
46524         else if(ps instanceof Array){
46525             for(var i =0, len = ps.length; i < len; i++){
46526                 this.addTypedPanels(lr, ps[i]);
46527             }
46528         }
46529         else if(!ps.events){ // raw config?
46530             var el = ps.el;
46531             delete ps.el; // prevent conflict
46532             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
46533         }
46534         else {  // panel object assumed!
46535             lr.add(ps);
46536         }
46537     },
46538     /**
46539      * Adds a xtype elements to the layout.
46540      * <pre><code>
46541
46542 layout.addxtype({
46543        xtype : 'ContentPanel',
46544        region: 'west',
46545        items: [ .... ]
46546    }
46547 );
46548
46549 layout.addxtype({
46550         xtype : 'NestedLayoutPanel',
46551         region: 'west',
46552         layout: {
46553            center: { },
46554            west: { }   
46555         },
46556         items : [ ... list of content panels or nested layout panels.. ]
46557    }
46558 );
46559 </code></pre>
46560      * @param {Object} cfg Xtype definition of item to add.
46561      */
46562     addxtype : function(cfg)
46563     {
46564         // basically accepts a pannel...
46565         // can accept a layout region..!?!?
46566         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
46567         
46568         if (!cfg.xtype.match(/Panel$/)) {
46569             return false;
46570         }
46571         var ret = false;
46572         
46573         if (typeof(cfg.region) == 'undefined') {
46574             Roo.log("Failed to add Panel, region was not set");
46575             Roo.log(cfg);
46576             return false;
46577         }
46578         var region = cfg.region;
46579         delete cfg.region;
46580         
46581           
46582         var xitems = [];
46583         if (cfg.items) {
46584             xitems = cfg.items;
46585             delete cfg.items;
46586         }
46587         var nb = false;
46588         
46589         switch(cfg.xtype) 
46590         {
46591             case 'ContentPanel':  // ContentPanel (el, cfg)
46592             case 'ScrollPanel':  // ContentPanel (el, cfg)
46593                 if(cfg.autoCreate) {
46594                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
46595                 } else {
46596                     var el = this.el.createChild();
46597                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
46598                 }
46599                 
46600                 this.add(region, ret);
46601                 break;
46602             
46603             
46604             case 'TreePanel': // our new panel!
46605                 cfg.el = this.el.createChild();
46606                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
46607                 this.add(region, ret);
46608                 break;
46609             
46610             case 'NestedLayoutPanel': 
46611                 // create a new Layout (which is  a Border Layout...
46612                 var el = this.el.createChild();
46613                 var clayout = cfg.layout;
46614                 delete cfg.layout;
46615                 clayout.items   = clayout.items  || [];
46616                 // replace this exitems with the clayout ones..
46617                 xitems = clayout.items;
46618                  
46619                 
46620                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
46621                     cfg.background = false;
46622                 }
46623                 var layout = new Roo.BorderLayout(el, clayout);
46624                 
46625                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
46626                 //console.log('adding nested layout panel '  + cfg.toSource());
46627                 this.add(region, ret);
46628                 nb = {}; /// find first...
46629                 break;
46630                 
46631             case 'GridPanel': 
46632             
46633                 // needs grid and region
46634                 
46635                 //var el = this.getRegion(region).el.createChild();
46636                 var el = this.el.createChild();
46637                 // create the grid first...
46638                 
46639                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
46640                 delete cfg.grid;
46641                 if (region == 'center' && this.active ) {
46642                     cfg.background = false;
46643                 }
46644                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
46645                 
46646                 this.add(region, ret);
46647                 if (cfg.background) {
46648                     ret.on('activate', function(gp) {
46649                         if (!gp.grid.rendered) {
46650                             gp.grid.render();
46651                         }
46652                     });
46653                 } else {
46654                     grid.render();
46655                 }
46656                 break;
46657            
46658                
46659                 
46660                 
46661             default: 
46662                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
46663                 return null;
46664              // GridPanel (grid, cfg)
46665             
46666         }
46667         this.beginUpdate();
46668         // add children..
46669         var region = '';
46670         var abn = {};
46671         Roo.each(xitems, function(i)  {
46672             region = nb && i.region ? i.region : false;
46673             
46674             var add = ret.addxtype(i);
46675            
46676             if (region) {
46677                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
46678                 if (!i.background) {
46679                     abn[region] = nb[region] ;
46680                 }
46681             }
46682             
46683         });
46684         this.endUpdate();
46685
46686         // make the last non-background panel active..
46687         //if (nb) { Roo.log(abn); }
46688         if (nb) {
46689             
46690             for(var r in abn) {
46691                 region = this.getRegion(r);
46692                 if (region) {
46693                     // tried using nb[r], but it does not work..
46694                      
46695                     region.showPanel(abn[r]);
46696                    
46697                 }
46698             }
46699         }
46700         return ret;
46701         
46702     }
46703 });
46704
46705 /**
46706  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
46707  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
46708  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
46709  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
46710  * <pre><code>
46711 // shorthand
46712 var CP = Roo.ContentPanel;
46713
46714 var layout = Roo.BorderLayout.create({
46715     north: {
46716         initialSize: 25,
46717         titlebar: false,
46718         panels: [new CP("north", "North")]
46719     },
46720     west: {
46721         split:true,
46722         initialSize: 200,
46723         minSize: 175,
46724         maxSize: 400,
46725         titlebar: true,
46726         collapsible: true,
46727         panels: [new CP("west", {title: "West"})]
46728     },
46729     east: {
46730         split:true,
46731         initialSize: 202,
46732         minSize: 175,
46733         maxSize: 400,
46734         titlebar: true,
46735         collapsible: true,
46736         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
46737     },
46738     south: {
46739         split:true,
46740         initialSize: 100,
46741         minSize: 100,
46742         maxSize: 200,
46743         titlebar: true,
46744         collapsible: true,
46745         panels: [new CP("south", {title: "South", closable: true})]
46746     },
46747     center: {
46748         titlebar: true,
46749         autoScroll:true,
46750         resizeTabs: true,
46751         minTabWidth: 50,
46752         preferredTabWidth: 150,
46753         panels: [
46754             new CP("center1", {title: "Close Me", closable: true}),
46755             new CP("center2", {title: "Center Panel", closable: false})
46756         ]
46757     }
46758 }, document.body);
46759
46760 layout.getRegion("center").showPanel("center1");
46761 </code></pre>
46762  * @param config
46763  * @param targetEl
46764  */
46765 Roo.BorderLayout.create = function(config, targetEl){
46766     var layout = new Roo.BorderLayout(targetEl || document.body, config);
46767     layout.beginUpdate();
46768     var regions = Roo.BorderLayout.RegionFactory.validRegions;
46769     for(var j = 0, jlen = regions.length; j < jlen; j++){
46770         var lr = regions[j];
46771         if(layout.regions[lr] && config[lr].panels){
46772             var r = layout.regions[lr];
46773             var ps = config[lr].panels;
46774             layout.addTypedPanels(r, ps);
46775         }
46776     }
46777     layout.endUpdate();
46778     return layout;
46779 };
46780
46781 // private
46782 Roo.BorderLayout.RegionFactory = {
46783     // private
46784     validRegions : ["north","south","east","west","center"],
46785
46786     // private
46787     create : function(target, mgr, config){
46788         target = target.toLowerCase();
46789         if(config.lightweight || config.basic){
46790             return new Roo.BasicLayoutRegion(mgr, config, target);
46791         }
46792         switch(target){
46793             case "north":
46794                 return new Roo.NorthLayoutRegion(mgr, config);
46795             case "south":
46796                 return new Roo.SouthLayoutRegion(mgr, config);
46797             case "east":
46798                 return new Roo.EastLayoutRegion(mgr, config);
46799             case "west":
46800                 return new Roo.WestLayoutRegion(mgr, config);
46801             case "center":
46802                 return new Roo.CenterLayoutRegion(mgr, config);
46803         }
46804         throw 'Layout region "'+target+'" not supported.';
46805     }
46806 };/*
46807  * Based on:
46808  * Ext JS Library 1.1.1
46809  * Copyright(c) 2006-2007, Ext JS, LLC.
46810  *
46811  * Originally Released Under LGPL - original licence link has changed is not relivant.
46812  *
46813  * Fork - LGPL
46814  * <script type="text/javascript">
46815  */
46816  
46817 /**
46818  * @class Roo.BasicLayoutRegion
46819  * @extends Roo.util.Observable
46820  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
46821  * and does not have a titlebar, tabs or any other features. All it does is size and position 
46822  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
46823  */
46824 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
46825     this.mgr = mgr;
46826     this.position  = pos;
46827     this.events = {
46828         /**
46829          * @scope Roo.BasicLayoutRegion
46830          */
46831         
46832         /**
46833          * @event beforeremove
46834          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
46835          * @param {Roo.LayoutRegion} this
46836          * @param {Roo.ContentPanel} panel The panel
46837          * @param {Object} e The cancel event object
46838          */
46839         "beforeremove" : true,
46840         /**
46841          * @event invalidated
46842          * Fires when the layout for this region is changed.
46843          * @param {Roo.LayoutRegion} this
46844          */
46845         "invalidated" : true,
46846         /**
46847          * @event visibilitychange
46848          * Fires when this region is shown or hidden 
46849          * @param {Roo.LayoutRegion} this
46850          * @param {Boolean} visibility true or false
46851          */
46852         "visibilitychange" : true,
46853         /**
46854          * @event paneladded
46855          * Fires when a panel is added. 
46856          * @param {Roo.LayoutRegion} this
46857          * @param {Roo.ContentPanel} panel The panel
46858          */
46859         "paneladded" : true,
46860         /**
46861          * @event panelremoved
46862          * Fires when a panel is removed. 
46863          * @param {Roo.LayoutRegion} this
46864          * @param {Roo.ContentPanel} panel The panel
46865          */
46866         "panelremoved" : true,
46867         /**
46868          * @event collapsed
46869          * Fires when this region is collapsed.
46870          * @param {Roo.LayoutRegion} this
46871          */
46872         "collapsed" : true,
46873         /**
46874          * @event expanded
46875          * Fires when this region is expanded.
46876          * @param {Roo.LayoutRegion} this
46877          */
46878         "expanded" : true,
46879         /**
46880          * @event slideshow
46881          * Fires when this region is slid into view.
46882          * @param {Roo.LayoutRegion} this
46883          */
46884         "slideshow" : true,
46885         /**
46886          * @event slidehide
46887          * Fires when this region slides out of view. 
46888          * @param {Roo.LayoutRegion} this
46889          */
46890         "slidehide" : true,
46891         /**
46892          * @event panelactivated
46893          * Fires when a panel is activated. 
46894          * @param {Roo.LayoutRegion} this
46895          * @param {Roo.ContentPanel} panel The activated panel
46896          */
46897         "panelactivated" : true,
46898         /**
46899          * @event resized
46900          * Fires when the user resizes this region. 
46901          * @param {Roo.LayoutRegion} this
46902          * @param {Number} newSize The new size (width for east/west, height for north/south)
46903          */
46904         "resized" : true
46905     };
46906     /** A collection of panels in this region. @type Roo.util.MixedCollection */
46907     this.panels = new Roo.util.MixedCollection();
46908     this.panels.getKey = this.getPanelId.createDelegate(this);
46909     this.box = null;
46910     this.activePanel = null;
46911     // ensure listeners are added...
46912     
46913     if (config.listeners || config.events) {
46914         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
46915             listeners : config.listeners || {},
46916             events : config.events || {}
46917         });
46918     }
46919     
46920     if(skipConfig !== true){
46921         this.applyConfig(config);
46922     }
46923 };
46924
46925 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
46926     getPanelId : function(p){
46927         return p.getId();
46928     },
46929     
46930     applyConfig : function(config){
46931         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
46932         this.config = config;
46933         
46934     },
46935     
46936     /**
46937      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
46938      * the width, for horizontal (north, south) the height.
46939      * @param {Number} newSize The new width or height
46940      */
46941     resizeTo : function(newSize){
46942         var el = this.el ? this.el :
46943                  (this.activePanel ? this.activePanel.getEl() : null);
46944         if(el){
46945             switch(this.position){
46946                 case "east":
46947                 case "west":
46948                     el.setWidth(newSize);
46949                     this.fireEvent("resized", this, newSize);
46950                 break;
46951                 case "north":
46952                 case "south":
46953                     el.setHeight(newSize);
46954                     this.fireEvent("resized", this, newSize);
46955                 break;                
46956             }
46957         }
46958     },
46959     
46960     getBox : function(){
46961         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
46962     },
46963     
46964     getMargins : function(){
46965         return this.margins;
46966     },
46967     
46968     updateBox : function(box){
46969         this.box = box;
46970         var el = this.activePanel.getEl();
46971         el.dom.style.left = box.x + "px";
46972         el.dom.style.top = box.y + "px";
46973         this.activePanel.setSize(box.width, box.height);
46974     },
46975     
46976     /**
46977      * Returns the container element for this region.
46978      * @return {Roo.Element}
46979      */
46980     getEl : function(){
46981         return this.activePanel;
46982     },
46983     
46984     /**
46985      * Returns true if this region is currently visible.
46986      * @return {Boolean}
46987      */
46988     isVisible : function(){
46989         return this.activePanel ? true : false;
46990     },
46991     
46992     setActivePanel : function(panel){
46993         panel = this.getPanel(panel);
46994         if(this.activePanel && this.activePanel != panel){
46995             this.activePanel.setActiveState(false);
46996             this.activePanel.getEl().setLeftTop(-10000,-10000);
46997         }
46998         this.activePanel = panel;
46999         panel.setActiveState(true);
47000         if(this.box){
47001             panel.setSize(this.box.width, this.box.height);
47002         }
47003         this.fireEvent("panelactivated", this, panel);
47004         this.fireEvent("invalidated");
47005     },
47006     
47007     /**
47008      * Show the specified panel.
47009      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
47010      * @return {Roo.ContentPanel} The shown panel or null
47011      */
47012     showPanel : function(panel){
47013         if(panel = this.getPanel(panel)){
47014             this.setActivePanel(panel);
47015         }
47016         return panel;
47017     },
47018     
47019     /**
47020      * Get the active panel for this region.
47021      * @return {Roo.ContentPanel} The active panel or null
47022      */
47023     getActivePanel : function(){
47024         return this.activePanel;
47025     },
47026     
47027     /**
47028      * Add the passed ContentPanel(s)
47029      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
47030      * @return {Roo.ContentPanel} The panel added (if only one was added)
47031      */
47032     add : function(panel){
47033         if(arguments.length > 1){
47034             for(var i = 0, len = arguments.length; i < len; i++) {
47035                 this.add(arguments[i]);
47036             }
47037             return null;
47038         }
47039         if(this.hasPanel(panel)){
47040             this.showPanel(panel);
47041             return panel;
47042         }
47043         var el = panel.getEl();
47044         if(el.dom.parentNode != this.mgr.el.dom){
47045             this.mgr.el.dom.appendChild(el.dom);
47046         }
47047         if(panel.setRegion){
47048             panel.setRegion(this);
47049         }
47050         this.panels.add(panel);
47051         el.setStyle("position", "absolute");
47052         if(!panel.background){
47053             this.setActivePanel(panel);
47054             if(this.config.initialSize && this.panels.getCount()==1){
47055                 this.resizeTo(this.config.initialSize);
47056             }
47057         }
47058         this.fireEvent("paneladded", this, panel);
47059         return panel;
47060     },
47061     
47062     /**
47063      * Returns true if the panel is in this region.
47064      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
47065      * @return {Boolean}
47066      */
47067     hasPanel : function(panel){
47068         if(typeof panel == "object"){ // must be panel obj
47069             panel = panel.getId();
47070         }
47071         return this.getPanel(panel) ? true : false;
47072     },
47073     
47074     /**
47075      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
47076      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
47077      * @param {Boolean} preservePanel Overrides the config preservePanel option
47078      * @return {Roo.ContentPanel} The panel that was removed
47079      */
47080     remove : function(panel, preservePanel){
47081         panel = this.getPanel(panel);
47082         if(!panel){
47083             return null;
47084         }
47085         var e = {};
47086         this.fireEvent("beforeremove", this, panel, e);
47087         if(e.cancel === true){
47088             return null;
47089         }
47090         var panelId = panel.getId();
47091         this.panels.removeKey(panelId);
47092         return panel;
47093     },
47094     
47095     /**
47096      * Returns the panel specified or null if it's not in this region.
47097      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
47098      * @return {Roo.ContentPanel}
47099      */
47100     getPanel : function(id){
47101         if(typeof id == "object"){ // must be panel obj
47102             return id;
47103         }
47104         return this.panels.get(id);
47105     },
47106     
47107     /**
47108      * Returns this regions position (north/south/east/west/center).
47109      * @return {String} 
47110      */
47111     getPosition: function(){
47112         return this.position;    
47113     }
47114 });/*
47115  * Based on:
47116  * Ext JS Library 1.1.1
47117  * Copyright(c) 2006-2007, Ext JS, LLC.
47118  *
47119  * Originally Released Under LGPL - original licence link has changed is not relivant.
47120  *
47121  * Fork - LGPL
47122  * <script type="text/javascript">
47123  */
47124  
47125 /**
47126  * @class Roo.LayoutRegion
47127  * @extends Roo.BasicLayoutRegion
47128  * This class represents a region in a layout manager.
47129  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
47130  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
47131  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
47132  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
47133  * @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})
47134  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
47135  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
47136  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
47137  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
47138  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
47139  * @cfg {String}    title           The title for the region (overrides panel titles)
47140  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
47141  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
47142  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
47143  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
47144  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
47145  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
47146  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
47147  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
47148  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
47149  * @cfg {Boolean}   showPin         True to show a pin button
47150  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
47151  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
47152  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
47153  * @cfg {Number}    width           For East/West panels
47154  * @cfg {Number}    height          For North/South panels
47155  * @cfg {Boolean}   split           To show the splitter
47156  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
47157  */
47158 Roo.LayoutRegion = function(mgr, config, pos){
47159     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
47160     var dh = Roo.DomHelper;
47161     /** This region's container element 
47162     * @type Roo.Element */
47163     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
47164     /** This region's title element 
47165     * @type Roo.Element */
47166
47167     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
47168         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
47169         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
47170     ]}, true);
47171     this.titleEl.enableDisplayMode();
47172     /** This region's title text element 
47173     * @type HTMLElement */
47174     this.titleTextEl = this.titleEl.dom.firstChild;
47175     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
47176     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
47177     this.closeBtn.enableDisplayMode();
47178     this.closeBtn.on("click", this.closeClicked, this);
47179     this.closeBtn.hide();
47180
47181     this.createBody(config);
47182     this.visible = true;
47183     this.collapsed = false;
47184
47185     if(config.hideWhenEmpty){
47186         this.hide();
47187         this.on("paneladded", this.validateVisibility, this);
47188         this.on("panelremoved", this.validateVisibility, this);
47189     }
47190     this.applyConfig(config);
47191 };
47192
47193 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
47194
47195     createBody : function(){
47196         /** This region's body element 
47197         * @type Roo.Element */
47198         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
47199     },
47200
47201     applyConfig : function(c){
47202         if(c.collapsible && this.position != "center" && !this.collapsedEl){
47203             var dh = Roo.DomHelper;
47204             if(c.titlebar !== false){
47205                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
47206                 this.collapseBtn.on("click", this.collapse, this);
47207                 this.collapseBtn.enableDisplayMode();
47208
47209                 if(c.showPin === true || this.showPin){
47210                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
47211                     this.stickBtn.enableDisplayMode();
47212                     this.stickBtn.on("click", this.expand, this);
47213                     this.stickBtn.hide();
47214                 }
47215             }
47216             /** This region's collapsed element
47217             * @type Roo.Element */
47218             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
47219                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
47220             ]}, true);
47221             if(c.floatable !== false){
47222                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
47223                this.collapsedEl.on("click", this.collapseClick, this);
47224             }
47225
47226             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
47227                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
47228                    id: "message", unselectable: "on", style:{"float":"left"}});
47229                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
47230              }
47231             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
47232             this.expandBtn.on("click", this.expand, this);
47233         }
47234         if(this.collapseBtn){
47235             this.collapseBtn.setVisible(c.collapsible == true);
47236         }
47237         this.cmargins = c.cmargins || this.cmargins ||
47238                          (this.position == "west" || this.position == "east" ?
47239                              {top: 0, left: 2, right:2, bottom: 0} :
47240                              {top: 2, left: 0, right:0, bottom: 2});
47241         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
47242         this.bottomTabs = c.tabPosition != "top";
47243         this.autoScroll = c.autoScroll || false;
47244         if(this.autoScroll){
47245             this.bodyEl.setStyle("overflow", "auto");
47246         }else{
47247             this.bodyEl.setStyle("overflow", "hidden");
47248         }
47249         //if(c.titlebar !== false){
47250             if((!c.titlebar && !c.title) || c.titlebar === false){
47251                 this.titleEl.hide();
47252             }else{
47253                 this.titleEl.show();
47254                 if(c.title){
47255                     this.titleTextEl.innerHTML = c.title;
47256                 }
47257             }
47258         //}
47259         this.duration = c.duration || .30;
47260         this.slideDuration = c.slideDuration || .45;
47261         this.config = c;
47262         if(c.collapsed){
47263             this.collapse(true);
47264         }
47265         if(c.hidden){
47266             this.hide();
47267         }
47268     },
47269     /**
47270      * Returns true if this region is currently visible.
47271      * @return {Boolean}
47272      */
47273     isVisible : function(){
47274         return this.visible;
47275     },
47276
47277     /**
47278      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
47279      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
47280      */
47281     setCollapsedTitle : function(title){
47282         title = title || "&#160;";
47283         if(this.collapsedTitleTextEl){
47284             this.collapsedTitleTextEl.innerHTML = title;
47285         }
47286     },
47287
47288     getBox : function(){
47289         var b;
47290         if(!this.collapsed){
47291             b = this.el.getBox(false, true);
47292         }else{
47293             b = this.collapsedEl.getBox(false, true);
47294         }
47295         return b;
47296     },
47297
47298     getMargins : function(){
47299         return this.collapsed ? this.cmargins : this.margins;
47300     },
47301
47302     highlight : function(){
47303         this.el.addClass("x-layout-panel-dragover");
47304     },
47305
47306     unhighlight : function(){
47307         this.el.removeClass("x-layout-panel-dragover");
47308     },
47309
47310     updateBox : function(box){
47311         this.box = box;
47312         if(!this.collapsed){
47313             this.el.dom.style.left = box.x + "px";
47314             this.el.dom.style.top = box.y + "px";
47315             this.updateBody(box.width, box.height);
47316         }else{
47317             this.collapsedEl.dom.style.left = box.x + "px";
47318             this.collapsedEl.dom.style.top = box.y + "px";
47319             this.collapsedEl.setSize(box.width, box.height);
47320         }
47321         if(this.tabs){
47322             this.tabs.autoSizeTabs();
47323         }
47324     },
47325
47326     updateBody : function(w, h){
47327         if(w !== null){
47328             this.el.setWidth(w);
47329             w -= this.el.getBorderWidth("rl");
47330             if(this.config.adjustments){
47331                 w += this.config.adjustments[0];
47332             }
47333         }
47334         if(h !== null){
47335             this.el.setHeight(h);
47336             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
47337             h -= this.el.getBorderWidth("tb");
47338             if(this.config.adjustments){
47339                 h += this.config.adjustments[1];
47340             }
47341             this.bodyEl.setHeight(h);
47342             if(this.tabs){
47343                 h = this.tabs.syncHeight(h);
47344             }
47345         }
47346         if(this.panelSize){
47347             w = w !== null ? w : this.panelSize.width;
47348             h = h !== null ? h : this.panelSize.height;
47349         }
47350         if(this.activePanel){
47351             var el = this.activePanel.getEl();
47352             w = w !== null ? w : el.getWidth();
47353             h = h !== null ? h : el.getHeight();
47354             this.panelSize = {width: w, height: h};
47355             this.activePanel.setSize(w, h);
47356         }
47357         if(Roo.isIE && this.tabs){
47358             this.tabs.el.repaint();
47359         }
47360     },
47361
47362     /**
47363      * Returns the container element for this region.
47364      * @return {Roo.Element}
47365      */
47366     getEl : function(){
47367         return this.el;
47368     },
47369
47370     /**
47371      * Hides this region.
47372      */
47373     hide : function(){
47374         if(!this.collapsed){
47375             this.el.dom.style.left = "-2000px";
47376             this.el.hide();
47377         }else{
47378             this.collapsedEl.dom.style.left = "-2000px";
47379             this.collapsedEl.hide();
47380         }
47381         this.visible = false;
47382         this.fireEvent("visibilitychange", this, false);
47383     },
47384
47385     /**
47386      * Shows this region if it was previously hidden.
47387      */
47388     show : function(){
47389         if(!this.collapsed){
47390             this.el.show();
47391         }else{
47392             this.collapsedEl.show();
47393         }
47394         this.visible = true;
47395         this.fireEvent("visibilitychange", this, true);
47396     },
47397
47398     closeClicked : function(){
47399         if(this.activePanel){
47400             this.remove(this.activePanel);
47401         }
47402     },
47403
47404     collapseClick : function(e){
47405         if(this.isSlid){
47406            e.stopPropagation();
47407            this.slideIn();
47408         }else{
47409            e.stopPropagation();
47410            this.slideOut();
47411         }
47412     },
47413
47414     /**
47415      * Collapses this region.
47416      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
47417      */
47418     collapse : function(skipAnim){
47419         if(this.collapsed) return;
47420         this.collapsed = true;
47421         if(this.split){
47422             this.split.el.hide();
47423         }
47424         if(this.config.animate && skipAnim !== true){
47425             this.fireEvent("invalidated", this);
47426             this.animateCollapse();
47427         }else{
47428             this.el.setLocation(-20000,-20000);
47429             this.el.hide();
47430             this.collapsedEl.show();
47431             this.fireEvent("collapsed", this);
47432             this.fireEvent("invalidated", this);
47433         }
47434     },
47435
47436     animateCollapse : function(){
47437         // overridden
47438     },
47439
47440     /**
47441      * Expands this region if it was previously collapsed.
47442      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
47443      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
47444      */
47445     expand : function(e, skipAnim){
47446         if(e) e.stopPropagation();
47447         if(!this.collapsed || this.el.hasActiveFx()) return;
47448         if(this.isSlid){
47449             this.afterSlideIn();
47450             skipAnim = true;
47451         }
47452         this.collapsed = false;
47453         if(this.config.animate && skipAnim !== true){
47454             this.animateExpand();
47455         }else{
47456             this.el.show();
47457             if(this.split){
47458                 this.split.el.show();
47459             }
47460             this.collapsedEl.setLocation(-2000,-2000);
47461             this.collapsedEl.hide();
47462             this.fireEvent("invalidated", this);
47463             this.fireEvent("expanded", this);
47464         }
47465     },
47466
47467     animateExpand : function(){
47468         // overridden
47469     },
47470
47471     initTabs : function()
47472     {
47473         this.bodyEl.setStyle("overflow", "hidden");
47474         var ts = new Roo.TabPanel(
47475                 this.bodyEl.dom,
47476                 {
47477                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
47478                     disableTooltips: this.config.disableTabTips,
47479                     toolbar : this.config.toolbar
47480                 }
47481         );
47482         if(this.config.hideTabs){
47483             ts.stripWrap.setDisplayed(false);
47484         }
47485         this.tabs = ts;
47486         ts.resizeTabs = this.config.resizeTabs === true;
47487         ts.minTabWidth = this.config.minTabWidth || 40;
47488         ts.maxTabWidth = this.config.maxTabWidth || 250;
47489         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
47490         ts.monitorResize = false;
47491         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
47492         ts.bodyEl.addClass('x-layout-tabs-body');
47493         this.panels.each(this.initPanelAsTab, this);
47494     },
47495
47496     initPanelAsTab : function(panel){
47497         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
47498                     this.config.closeOnTab && panel.isClosable());
47499         if(panel.tabTip !== undefined){
47500             ti.setTooltip(panel.tabTip);
47501         }
47502         ti.on("activate", function(){
47503               this.setActivePanel(panel);
47504         }, this);
47505         if(this.config.closeOnTab){
47506             ti.on("beforeclose", function(t, e){
47507                 e.cancel = true;
47508                 this.remove(panel);
47509             }, this);
47510         }
47511         return ti;
47512     },
47513
47514     updatePanelTitle : function(panel, title){
47515         if(this.activePanel == panel){
47516             this.updateTitle(title);
47517         }
47518         if(this.tabs){
47519             var ti = this.tabs.getTab(panel.getEl().id);
47520             ti.setText(title);
47521             if(panel.tabTip !== undefined){
47522                 ti.setTooltip(panel.tabTip);
47523             }
47524         }
47525     },
47526
47527     updateTitle : function(title){
47528         if(this.titleTextEl && !this.config.title){
47529             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
47530         }
47531     },
47532
47533     setActivePanel : function(panel){
47534         panel = this.getPanel(panel);
47535         if(this.activePanel && this.activePanel != panel){
47536             this.activePanel.setActiveState(false);
47537         }
47538         this.activePanel = panel;
47539         panel.setActiveState(true);
47540         if(this.panelSize){
47541             panel.setSize(this.panelSize.width, this.panelSize.height);
47542         }
47543         if(this.closeBtn){
47544             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
47545         }
47546         this.updateTitle(panel.getTitle());
47547         if(this.tabs){
47548             this.fireEvent("invalidated", this);
47549         }
47550         this.fireEvent("panelactivated", this, panel);
47551     },
47552
47553     /**
47554      * Shows the specified panel.
47555      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
47556      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
47557      */
47558     showPanel : function(panel){
47559         if(panel = this.getPanel(panel)){
47560             if(this.tabs){
47561                 var tab = this.tabs.getTab(panel.getEl().id);
47562                 if(tab.isHidden()){
47563                     this.tabs.unhideTab(tab.id);
47564                 }
47565                 tab.activate();
47566             }else{
47567                 this.setActivePanel(panel);
47568             }
47569         }
47570         return panel;
47571     },
47572
47573     /**
47574      * Get the active panel for this region.
47575      * @return {Roo.ContentPanel} The active panel or null
47576      */
47577     getActivePanel : function(){
47578         return this.activePanel;
47579     },
47580
47581     validateVisibility : function(){
47582         if(this.panels.getCount() < 1){
47583             this.updateTitle("&#160;");
47584             this.closeBtn.hide();
47585             this.hide();
47586         }else{
47587             if(!this.isVisible()){
47588                 this.show();
47589             }
47590         }
47591     },
47592
47593     /**
47594      * Adds the passed ContentPanel(s) to this region.
47595      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
47596      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
47597      */
47598     add : function(panel){
47599         if(arguments.length > 1){
47600             for(var i = 0, len = arguments.length; i < len; i++) {
47601                 this.add(arguments[i]);
47602             }
47603             return null;
47604         }
47605         if(this.hasPanel(panel)){
47606             this.showPanel(panel);
47607             return panel;
47608         }
47609         panel.setRegion(this);
47610         this.panels.add(panel);
47611         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
47612             this.bodyEl.dom.appendChild(panel.getEl().dom);
47613             if(panel.background !== true){
47614                 this.setActivePanel(panel);
47615             }
47616             this.fireEvent("paneladded", this, panel);
47617             return panel;
47618         }
47619         if(!this.tabs){
47620             this.initTabs();
47621         }else{
47622             this.initPanelAsTab(panel);
47623         }
47624         if(panel.background !== true){
47625             this.tabs.activate(panel.getEl().id);
47626         }
47627         this.fireEvent("paneladded", this, panel);
47628         return panel;
47629     },
47630
47631     /**
47632      * Hides the tab for the specified panel.
47633      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
47634      */
47635     hidePanel : function(panel){
47636         if(this.tabs && (panel = this.getPanel(panel))){
47637             this.tabs.hideTab(panel.getEl().id);
47638         }
47639     },
47640
47641     /**
47642      * Unhides the tab for a previously hidden panel.
47643      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
47644      */
47645     unhidePanel : function(panel){
47646         if(this.tabs && (panel = this.getPanel(panel))){
47647             this.tabs.unhideTab(panel.getEl().id);
47648         }
47649     },
47650
47651     clearPanels : function(){
47652         while(this.panels.getCount() > 0){
47653              this.remove(this.panels.first());
47654         }
47655     },
47656
47657     /**
47658      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
47659      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
47660      * @param {Boolean} preservePanel Overrides the config preservePanel option
47661      * @return {Roo.ContentPanel} The panel that was removed
47662      */
47663     remove : function(panel, preservePanel){
47664         panel = this.getPanel(panel);
47665         if(!panel){
47666             return null;
47667         }
47668         var e = {};
47669         this.fireEvent("beforeremove", this, panel, e);
47670         if(e.cancel === true){
47671             return null;
47672         }
47673         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
47674         var panelId = panel.getId();
47675         this.panels.removeKey(panelId);
47676         if(preservePanel){
47677             document.body.appendChild(panel.getEl().dom);
47678         }
47679         if(this.tabs){
47680             this.tabs.removeTab(panel.getEl().id);
47681         }else if (!preservePanel){
47682             this.bodyEl.dom.removeChild(panel.getEl().dom);
47683         }
47684         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
47685             var p = this.panels.first();
47686             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
47687             tempEl.appendChild(p.getEl().dom);
47688             this.bodyEl.update("");
47689             this.bodyEl.dom.appendChild(p.getEl().dom);
47690             tempEl = null;
47691             this.updateTitle(p.getTitle());
47692             this.tabs = null;
47693             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
47694             this.setActivePanel(p);
47695         }
47696         panel.setRegion(null);
47697         if(this.activePanel == panel){
47698             this.activePanel = null;
47699         }
47700         if(this.config.autoDestroy !== false && preservePanel !== true){
47701             try{panel.destroy();}catch(e){}
47702         }
47703         this.fireEvent("panelremoved", this, panel);
47704         return panel;
47705     },
47706
47707     /**
47708      * Returns the TabPanel component used by this region
47709      * @return {Roo.TabPanel}
47710      */
47711     getTabs : function(){
47712         return this.tabs;
47713     },
47714
47715     createTool : function(parentEl, className){
47716         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
47717             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
47718         btn.addClassOnOver("x-layout-tools-button-over");
47719         return btn;
47720     }
47721 });/*
47722  * Based on:
47723  * Ext JS Library 1.1.1
47724  * Copyright(c) 2006-2007, Ext JS, LLC.
47725  *
47726  * Originally Released Under LGPL - original licence link has changed is not relivant.
47727  *
47728  * Fork - LGPL
47729  * <script type="text/javascript">
47730  */
47731  
47732
47733
47734 /**
47735  * @class Roo.SplitLayoutRegion
47736  * @extends Roo.LayoutRegion
47737  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
47738  */
47739 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
47740     this.cursor = cursor;
47741     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
47742 };
47743
47744 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
47745     splitTip : "Drag to resize.",
47746     collapsibleSplitTip : "Drag to resize. Double click to hide.",
47747     useSplitTips : false,
47748
47749     applyConfig : function(config){
47750         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
47751         if(config.split){
47752             if(!this.split){
47753                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
47754                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
47755                 /** The SplitBar for this region 
47756                 * @type Roo.SplitBar */
47757                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
47758                 this.split.on("moved", this.onSplitMove, this);
47759                 this.split.useShim = config.useShim === true;
47760                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
47761                 if(this.useSplitTips){
47762                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
47763                 }
47764                 if(config.collapsible){
47765                     this.split.el.on("dblclick", this.collapse,  this);
47766                 }
47767             }
47768             if(typeof config.minSize != "undefined"){
47769                 this.split.minSize = config.minSize;
47770             }
47771             if(typeof config.maxSize != "undefined"){
47772                 this.split.maxSize = config.maxSize;
47773             }
47774             if(config.hideWhenEmpty || config.hidden || config.collapsed){
47775                 this.hideSplitter();
47776             }
47777         }
47778     },
47779
47780     getHMaxSize : function(){
47781          var cmax = this.config.maxSize || 10000;
47782          var center = this.mgr.getRegion("center");
47783          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
47784     },
47785
47786     getVMaxSize : function(){
47787          var cmax = this.config.maxSize || 10000;
47788          var center = this.mgr.getRegion("center");
47789          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
47790     },
47791
47792     onSplitMove : function(split, newSize){
47793         this.fireEvent("resized", this, newSize);
47794     },
47795     
47796     /** 
47797      * Returns the {@link Roo.SplitBar} for this region.
47798      * @return {Roo.SplitBar}
47799      */
47800     getSplitBar : function(){
47801         return this.split;
47802     },
47803     
47804     hide : function(){
47805         this.hideSplitter();
47806         Roo.SplitLayoutRegion.superclass.hide.call(this);
47807     },
47808
47809     hideSplitter : function(){
47810         if(this.split){
47811             this.split.el.setLocation(-2000,-2000);
47812             this.split.el.hide();
47813         }
47814     },
47815
47816     show : function(){
47817         if(this.split){
47818             this.split.el.show();
47819         }
47820         Roo.SplitLayoutRegion.superclass.show.call(this);
47821     },
47822     
47823     beforeSlide: function(){
47824         if(Roo.isGecko){// firefox overflow auto bug workaround
47825             this.bodyEl.clip();
47826             if(this.tabs) this.tabs.bodyEl.clip();
47827             if(this.activePanel){
47828                 this.activePanel.getEl().clip();
47829                 
47830                 if(this.activePanel.beforeSlide){
47831                     this.activePanel.beforeSlide();
47832                 }
47833             }
47834         }
47835     },
47836     
47837     afterSlide : function(){
47838         if(Roo.isGecko){// firefox overflow auto bug workaround
47839             this.bodyEl.unclip();
47840             if(this.tabs) this.tabs.bodyEl.unclip();
47841             if(this.activePanel){
47842                 this.activePanel.getEl().unclip();
47843                 if(this.activePanel.afterSlide){
47844                     this.activePanel.afterSlide();
47845                 }
47846             }
47847         }
47848     },
47849
47850     initAutoHide : function(){
47851         if(this.autoHide !== false){
47852             if(!this.autoHideHd){
47853                 var st = new Roo.util.DelayedTask(this.slideIn, this);
47854                 this.autoHideHd = {
47855                     "mouseout": function(e){
47856                         if(!e.within(this.el, true)){
47857                             st.delay(500);
47858                         }
47859                     },
47860                     "mouseover" : function(e){
47861                         st.cancel();
47862                     },
47863                     scope : this
47864                 };
47865             }
47866             this.el.on(this.autoHideHd);
47867         }
47868     },
47869
47870     clearAutoHide : function(){
47871         if(this.autoHide !== false){
47872             this.el.un("mouseout", this.autoHideHd.mouseout);
47873             this.el.un("mouseover", this.autoHideHd.mouseover);
47874         }
47875     },
47876
47877     clearMonitor : function(){
47878         Roo.get(document).un("click", this.slideInIf, this);
47879     },
47880
47881     // these names are backwards but not changed for compat
47882     slideOut : function(){
47883         if(this.isSlid || this.el.hasActiveFx()){
47884             return;
47885         }
47886         this.isSlid = true;
47887         if(this.collapseBtn){
47888             this.collapseBtn.hide();
47889         }
47890         this.closeBtnState = this.closeBtn.getStyle('display');
47891         this.closeBtn.hide();
47892         if(this.stickBtn){
47893             this.stickBtn.show();
47894         }
47895         this.el.show();
47896         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
47897         this.beforeSlide();
47898         this.el.setStyle("z-index", 10001);
47899         this.el.slideIn(this.getSlideAnchor(), {
47900             callback: function(){
47901                 this.afterSlide();
47902                 this.initAutoHide();
47903                 Roo.get(document).on("click", this.slideInIf, this);
47904                 this.fireEvent("slideshow", this);
47905             },
47906             scope: this,
47907             block: true
47908         });
47909     },
47910
47911     afterSlideIn : function(){
47912         this.clearAutoHide();
47913         this.isSlid = false;
47914         this.clearMonitor();
47915         this.el.setStyle("z-index", "");
47916         if(this.collapseBtn){
47917             this.collapseBtn.show();
47918         }
47919         this.closeBtn.setStyle('display', this.closeBtnState);
47920         if(this.stickBtn){
47921             this.stickBtn.hide();
47922         }
47923         this.fireEvent("slidehide", this);
47924     },
47925
47926     slideIn : function(cb){
47927         if(!this.isSlid || this.el.hasActiveFx()){
47928             Roo.callback(cb);
47929             return;
47930         }
47931         this.isSlid = false;
47932         this.beforeSlide();
47933         this.el.slideOut(this.getSlideAnchor(), {
47934             callback: function(){
47935                 this.el.setLeftTop(-10000, -10000);
47936                 this.afterSlide();
47937                 this.afterSlideIn();
47938                 Roo.callback(cb);
47939             },
47940             scope: this,
47941             block: true
47942         });
47943     },
47944     
47945     slideInIf : function(e){
47946         if(!e.within(this.el)){
47947             this.slideIn();
47948         }
47949     },
47950
47951     animateCollapse : function(){
47952         this.beforeSlide();
47953         this.el.setStyle("z-index", 20000);
47954         var anchor = this.getSlideAnchor();
47955         this.el.slideOut(anchor, {
47956             callback : function(){
47957                 this.el.setStyle("z-index", "");
47958                 this.collapsedEl.slideIn(anchor, {duration:.3});
47959                 this.afterSlide();
47960                 this.el.setLocation(-10000,-10000);
47961                 this.el.hide();
47962                 this.fireEvent("collapsed", this);
47963             },
47964             scope: this,
47965             block: true
47966         });
47967     },
47968
47969     animateExpand : function(){
47970         this.beforeSlide();
47971         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
47972         this.el.setStyle("z-index", 20000);
47973         this.collapsedEl.hide({
47974             duration:.1
47975         });
47976         this.el.slideIn(this.getSlideAnchor(), {
47977             callback : function(){
47978                 this.el.setStyle("z-index", "");
47979                 this.afterSlide();
47980                 if(this.split){
47981                     this.split.el.show();
47982                 }
47983                 this.fireEvent("invalidated", this);
47984                 this.fireEvent("expanded", this);
47985             },
47986             scope: this,
47987             block: true
47988         });
47989     },
47990
47991     anchors : {
47992         "west" : "left",
47993         "east" : "right",
47994         "north" : "top",
47995         "south" : "bottom"
47996     },
47997
47998     sanchors : {
47999         "west" : "l",
48000         "east" : "r",
48001         "north" : "t",
48002         "south" : "b"
48003     },
48004
48005     canchors : {
48006         "west" : "tl-tr",
48007         "east" : "tr-tl",
48008         "north" : "tl-bl",
48009         "south" : "bl-tl"
48010     },
48011
48012     getAnchor : function(){
48013         return this.anchors[this.position];
48014     },
48015
48016     getCollapseAnchor : function(){
48017         return this.canchors[this.position];
48018     },
48019
48020     getSlideAnchor : function(){
48021         return this.sanchors[this.position];
48022     },
48023
48024     getAlignAdj : function(){
48025         var cm = this.cmargins;
48026         switch(this.position){
48027             case "west":
48028                 return [0, 0];
48029             break;
48030             case "east":
48031                 return [0, 0];
48032             break;
48033             case "north":
48034                 return [0, 0];
48035             break;
48036             case "south":
48037                 return [0, 0];
48038             break;
48039         }
48040     },
48041
48042     getExpandAdj : function(){
48043         var c = this.collapsedEl, cm = this.cmargins;
48044         switch(this.position){
48045             case "west":
48046                 return [-(cm.right+c.getWidth()+cm.left), 0];
48047             break;
48048             case "east":
48049                 return [cm.right+c.getWidth()+cm.left, 0];
48050             break;
48051             case "north":
48052                 return [0, -(cm.top+cm.bottom+c.getHeight())];
48053             break;
48054             case "south":
48055                 return [0, cm.top+cm.bottom+c.getHeight()];
48056             break;
48057         }
48058     }
48059 });/*
48060  * Based on:
48061  * Ext JS Library 1.1.1
48062  * Copyright(c) 2006-2007, Ext JS, LLC.
48063  *
48064  * Originally Released Under LGPL - original licence link has changed is not relivant.
48065  *
48066  * Fork - LGPL
48067  * <script type="text/javascript">
48068  */
48069 /*
48070  * These classes are private internal classes
48071  */
48072 Roo.CenterLayoutRegion = function(mgr, config){
48073     Roo.LayoutRegion.call(this, mgr, config, "center");
48074     this.visible = true;
48075     this.minWidth = config.minWidth || 20;
48076     this.minHeight = config.minHeight || 20;
48077 };
48078
48079 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
48080     hide : function(){
48081         // center panel can't be hidden
48082     },
48083     
48084     show : function(){
48085         // center panel can't be hidden
48086     },
48087     
48088     getMinWidth: function(){
48089         return this.minWidth;
48090     },
48091     
48092     getMinHeight: function(){
48093         return this.minHeight;
48094     }
48095 });
48096
48097
48098 Roo.NorthLayoutRegion = function(mgr, config){
48099     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
48100     if(this.split){
48101         this.split.placement = Roo.SplitBar.TOP;
48102         this.split.orientation = Roo.SplitBar.VERTICAL;
48103         this.split.el.addClass("x-layout-split-v");
48104     }
48105     var size = config.initialSize || config.height;
48106     if(typeof size != "undefined"){
48107         this.el.setHeight(size);
48108     }
48109 };
48110 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
48111     orientation: Roo.SplitBar.VERTICAL,
48112     getBox : function(){
48113         if(this.collapsed){
48114             return this.collapsedEl.getBox();
48115         }
48116         var box = this.el.getBox();
48117         if(this.split){
48118             box.height += this.split.el.getHeight();
48119         }
48120         return box;
48121     },
48122     
48123     updateBox : function(box){
48124         if(this.split && !this.collapsed){
48125             box.height -= this.split.el.getHeight();
48126             this.split.el.setLeft(box.x);
48127             this.split.el.setTop(box.y+box.height);
48128             this.split.el.setWidth(box.width);
48129         }
48130         if(this.collapsed){
48131             this.updateBody(box.width, null);
48132         }
48133         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48134     }
48135 });
48136
48137 Roo.SouthLayoutRegion = function(mgr, config){
48138     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
48139     if(this.split){
48140         this.split.placement = Roo.SplitBar.BOTTOM;
48141         this.split.orientation = Roo.SplitBar.VERTICAL;
48142         this.split.el.addClass("x-layout-split-v");
48143     }
48144     var size = config.initialSize || config.height;
48145     if(typeof size != "undefined"){
48146         this.el.setHeight(size);
48147     }
48148 };
48149 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
48150     orientation: Roo.SplitBar.VERTICAL,
48151     getBox : function(){
48152         if(this.collapsed){
48153             return this.collapsedEl.getBox();
48154         }
48155         var box = this.el.getBox();
48156         if(this.split){
48157             var sh = this.split.el.getHeight();
48158             box.height += sh;
48159             box.y -= sh;
48160         }
48161         return box;
48162     },
48163     
48164     updateBox : function(box){
48165         if(this.split && !this.collapsed){
48166             var sh = this.split.el.getHeight();
48167             box.height -= sh;
48168             box.y += sh;
48169             this.split.el.setLeft(box.x);
48170             this.split.el.setTop(box.y-sh);
48171             this.split.el.setWidth(box.width);
48172         }
48173         if(this.collapsed){
48174             this.updateBody(box.width, null);
48175         }
48176         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48177     }
48178 });
48179
48180 Roo.EastLayoutRegion = function(mgr, config){
48181     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
48182     if(this.split){
48183         this.split.placement = Roo.SplitBar.RIGHT;
48184         this.split.orientation = Roo.SplitBar.HORIZONTAL;
48185         this.split.el.addClass("x-layout-split-h");
48186     }
48187     var size = config.initialSize || config.width;
48188     if(typeof size != "undefined"){
48189         this.el.setWidth(size);
48190     }
48191 };
48192 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
48193     orientation: Roo.SplitBar.HORIZONTAL,
48194     getBox : function(){
48195         if(this.collapsed){
48196             return this.collapsedEl.getBox();
48197         }
48198         var box = this.el.getBox();
48199         if(this.split){
48200             var sw = this.split.el.getWidth();
48201             box.width += sw;
48202             box.x -= sw;
48203         }
48204         return box;
48205     },
48206
48207     updateBox : function(box){
48208         if(this.split && !this.collapsed){
48209             var sw = this.split.el.getWidth();
48210             box.width -= sw;
48211             this.split.el.setLeft(box.x);
48212             this.split.el.setTop(box.y);
48213             this.split.el.setHeight(box.height);
48214             box.x += sw;
48215         }
48216         if(this.collapsed){
48217             this.updateBody(null, box.height);
48218         }
48219         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48220     }
48221 });
48222
48223 Roo.WestLayoutRegion = function(mgr, config){
48224     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
48225     if(this.split){
48226         this.split.placement = Roo.SplitBar.LEFT;
48227         this.split.orientation = Roo.SplitBar.HORIZONTAL;
48228         this.split.el.addClass("x-layout-split-h");
48229     }
48230     var size = config.initialSize || config.width;
48231     if(typeof size != "undefined"){
48232         this.el.setWidth(size);
48233     }
48234 };
48235 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
48236     orientation: Roo.SplitBar.HORIZONTAL,
48237     getBox : function(){
48238         if(this.collapsed){
48239             return this.collapsedEl.getBox();
48240         }
48241         var box = this.el.getBox();
48242         if(this.split){
48243             box.width += this.split.el.getWidth();
48244         }
48245         return box;
48246     },
48247     
48248     updateBox : function(box){
48249         if(this.split && !this.collapsed){
48250             var sw = this.split.el.getWidth();
48251             box.width -= sw;
48252             this.split.el.setLeft(box.x+box.width);
48253             this.split.el.setTop(box.y);
48254             this.split.el.setHeight(box.height);
48255         }
48256         if(this.collapsed){
48257             this.updateBody(null, box.height);
48258         }
48259         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48260     }
48261 });
48262 /*
48263  * Based on:
48264  * Ext JS Library 1.1.1
48265  * Copyright(c) 2006-2007, Ext JS, LLC.
48266  *
48267  * Originally Released Under LGPL - original licence link has changed is not relivant.
48268  *
48269  * Fork - LGPL
48270  * <script type="text/javascript">
48271  */
48272  
48273  
48274 /*
48275  * Private internal class for reading and applying state
48276  */
48277 Roo.LayoutStateManager = function(layout){
48278      // default empty state
48279      this.state = {
48280         north: {},
48281         south: {},
48282         east: {},
48283         west: {}       
48284     };
48285 };
48286
48287 Roo.LayoutStateManager.prototype = {
48288     init : function(layout, provider){
48289         this.provider = provider;
48290         var state = provider.get(layout.id+"-layout-state");
48291         if(state){
48292             var wasUpdating = layout.isUpdating();
48293             if(!wasUpdating){
48294                 layout.beginUpdate();
48295             }
48296             for(var key in state){
48297                 if(typeof state[key] != "function"){
48298                     var rstate = state[key];
48299                     var r = layout.getRegion(key);
48300                     if(r && rstate){
48301                         if(rstate.size){
48302                             r.resizeTo(rstate.size);
48303                         }
48304                         if(rstate.collapsed == true){
48305                             r.collapse(true);
48306                         }else{
48307                             r.expand(null, true);
48308                         }
48309                     }
48310                 }
48311             }
48312             if(!wasUpdating){
48313                 layout.endUpdate();
48314             }
48315             this.state = state; 
48316         }
48317         this.layout = layout;
48318         layout.on("regionresized", this.onRegionResized, this);
48319         layout.on("regioncollapsed", this.onRegionCollapsed, this);
48320         layout.on("regionexpanded", this.onRegionExpanded, this);
48321     },
48322     
48323     storeState : function(){
48324         this.provider.set(this.layout.id+"-layout-state", this.state);
48325     },
48326     
48327     onRegionResized : function(region, newSize){
48328         this.state[region.getPosition()].size = newSize;
48329         this.storeState();
48330     },
48331     
48332     onRegionCollapsed : function(region){
48333         this.state[region.getPosition()].collapsed = true;
48334         this.storeState();
48335     },
48336     
48337     onRegionExpanded : function(region){
48338         this.state[region.getPosition()].collapsed = false;
48339         this.storeState();
48340     }
48341 };/*
48342  * Based on:
48343  * Ext JS Library 1.1.1
48344  * Copyright(c) 2006-2007, Ext JS, LLC.
48345  *
48346  * Originally Released Under LGPL - original licence link has changed is not relivant.
48347  *
48348  * Fork - LGPL
48349  * <script type="text/javascript">
48350  */
48351 /**
48352  * @class Roo.ContentPanel
48353  * @extends Roo.util.Observable
48354  * A basic ContentPanel element.
48355  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
48356  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
48357  * @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
48358  * @cfg {Boolean}   closable      True if the panel can be closed/removed
48359  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
48360  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
48361  * @cfg {Toolbar}   toolbar       A toolbar for this panel
48362  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
48363  * @cfg {String} title          The title for this panel
48364  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
48365  * @cfg {String} url            Calls {@link #setUrl} with this value
48366  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
48367  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
48368  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
48369  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
48370
48371  * @constructor
48372  * Create a new ContentPanel.
48373  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
48374  * @param {String/Object} config A string to set only the title or a config object
48375  * @param {String} content (optional) Set the HTML content for this panel
48376  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
48377  */
48378 Roo.ContentPanel = function(el, config, content){
48379     
48380      
48381     /*
48382     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
48383         config = el;
48384         el = Roo.id();
48385     }
48386     if (config && config.parentLayout) { 
48387         el = config.parentLayout.el.createChild(); 
48388     }
48389     */
48390     if(el.autoCreate){ // xtype is available if this is called from factory
48391         config = el;
48392         el = Roo.id();
48393     }
48394     this.el = Roo.get(el);
48395     if(!this.el && config && config.autoCreate){
48396         if(typeof config.autoCreate == "object"){
48397             if(!config.autoCreate.id){
48398                 config.autoCreate.id = config.id||el;
48399             }
48400             this.el = Roo.DomHelper.append(document.body,
48401                         config.autoCreate, true);
48402         }else{
48403             this.el = Roo.DomHelper.append(document.body,
48404                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
48405         }
48406     }
48407     this.closable = false;
48408     this.loaded = false;
48409     this.active = false;
48410     if(typeof config == "string"){
48411         this.title = config;
48412     }else{
48413         Roo.apply(this, config);
48414     }
48415     
48416     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
48417         this.wrapEl = this.el.wrap();
48418         this.toolbar.container = this.el.insertSibling(false, 'before');
48419         this.toolbar = new Roo.Toolbar(this.toolbar);
48420     }
48421     
48422     // xtype created footer. - not sure if will work as we normally have to render first..
48423     if (this.footer && !this.footer.el && this.footer.xtype) {
48424         if (!this.wrapEl) {
48425             this.wrapEl = this.el.wrap();
48426         }
48427     
48428         this.footer.container = this.wrapEl.createChild();
48429          
48430         this.footer = Roo.factory(this.footer, Roo);
48431         
48432     }
48433     
48434     if(this.resizeEl){
48435         this.resizeEl = Roo.get(this.resizeEl, true);
48436     }else{
48437         this.resizeEl = this.el;
48438     }
48439     // handle view.xtype
48440     
48441     if (this.view && typeof(this.view.xtype) != 'undefined') {
48442         this.view.el = this.el.appendChild(document.createElement("div"));
48443         this.view = Roo.factory(this.view);
48444         this.view.render && this.view.render(false, ''); // render blank..
48445     }
48446     
48447     
48448     
48449     this.addEvents({
48450         /**
48451          * @event activate
48452          * Fires when this panel is activated. 
48453          * @param {Roo.ContentPanel} this
48454          */
48455         "activate" : true,
48456         /**
48457          * @event deactivate
48458          * Fires when this panel is activated. 
48459          * @param {Roo.ContentPanel} this
48460          */
48461         "deactivate" : true,
48462
48463         /**
48464          * @event resize
48465          * Fires when this panel is resized if fitToFrame is true.
48466          * @param {Roo.ContentPanel} this
48467          * @param {Number} width The width after any component adjustments
48468          * @param {Number} height The height after any component adjustments
48469          */
48470         "resize" : true,
48471         
48472          /**
48473          * @event render
48474          * Fires when this tab is created
48475          * @param {Roo.ContentPanel} this
48476          */
48477         "render" : true
48478         
48479         
48480         
48481     });
48482     if(this.autoScroll){
48483         this.resizeEl.setStyle("overflow", "auto");
48484     } else {
48485         // fix randome scrolling
48486         this.el.on('scroll', function() {
48487             Roo.log('fix random scolling');
48488             this.scrollTo('top',0); 
48489         });
48490     }
48491     content = content || this.content;
48492     if(content){
48493         this.setContent(content);
48494     }
48495     if(config && config.url){
48496         this.setUrl(this.url, this.params, this.loadOnce);
48497     }
48498     
48499     
48500     
48501     Roo.ContentPanel.superclass.constructor.call(this);
48502     
48503     this.fireEvent('render', this);
48504 };
48505
48506 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
48507     tabTip:'',
48508     setRegion : function(region){
48509         this.region = region;
48510         if(region){
48511            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
48512         }else{
48513            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
48514         } 
48515     },
48516     
48517     /**
48518      * Returns the toolbar for this Panel if one was configured. 
48519      * @return {Roo.Toolbar} 
48520      */
48521     getToolbar : function(){
48522         return this.toolbar;
48523     },
48524     
48525     setActiveState : function(active){
48526         this.active = active;
48527         if(!active){
48528             this.fireEvent("deactivate", this);
48529         }else{
48530             this.fireEvent("activate", this);
48531         }
48532     },
48533     /**
48534      * Updates this panel's element
48535      * @param {String} content The new content
48536      * @param {Boolean} loadScripts (optional) true to look for and process scripts
48537     */
48538     setContent : function(content, loadScripts){
48539         this.el.update(content, loadScripts);
48540     },
48541
48542     ignoreResize : function(w, h){
48543         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
48544             return true;
48545         }else{
48546             this.lastSize = {width: w, height: h};
48547             return false;
48548         }
48549     },
48550     /**
48551      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
48552      * @return {Roo.UpdateManager} The UpdateManager
48553      */
48554     getUpdateManager : function(){
48555         return this.el.getUpdateManager();
48556     },
48557      /**
48558      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
48559      * @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:
48560 <pre><code>
48561 panel.load({
48562     url: "your-url.php",
48563     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
48564     callback: yourFunction,
48565     scope: yourObject, //(optional scope)
48566     discardUrl: false,
48567     nocache: false,
48568     text: "Loading...",
48569     timeout: 30,
48570     scripts: false
48571 });
48572 </code></pre>
48573      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
48574      * 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.
48575      * @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}
48576      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
48577      * @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.
48578      * @return {Roo.ContentPanel} this
48579      */
48580     load : function(){
48581         var um = this.el.getUpdateManager();
48582         um.update.apply(um, arguments);
48583         return this;
48584     },
48585
48586
48587     /**
48588      * 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.
48589      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
48590      * @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)
48591      * @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)
48592      * @return {Roo.UpdateManager} The UpdateManager
48593      */
48594     setUrl : function(url, params, loadOnce){
48595         if(this.refreshDelegate){
48596             this.removeListener("activate", this.refreshDelegate);
48597         }
48598         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
48599         this.on("activate", this.refreshDelegate);
48600         return this.el.getUpdateManager();
48601     },
48602     
48603     _handleRefresh : function(url, params, loadOnce){
48604         if(!loadOnce || !this.loaded){
48605             var updater = this.el.getUpdateManager();
48606             updater.update(url, params, this._setLoaded.createDelegate(this));
48607         }
48608     },
48609     
48610     _setLoaded : function(){
48611         this.loaded = true;
48612     }, 
48613     
48614     /**
48615      * Returns this panel's id
48616      * @return {String} 
48617      */
48618     getId : function(){
48619         return this.el.id;
48620     },
48621     
48622     /** 
48623      * Returns this panel's element - used by regiosn to add.
48624      * @return {Roo.Element} 
48625      */
48626     getEl : function(){
48627         return this.wrapEl || this.el;
48628     },
48629     
48630     adjustForComponents : function(width, height)
48631     {
48632         //Roo.log('adjustForComponents ');
48633         if(this.resizeEl != this.el){
48634             width -= this.el.getFrameWidth('lr');
48635             height -= this.el.getFrameWidth('tb');
48636         }
48637         if(this.toolbar){
48638             var te = this.toolbar.getEl();
48639             height -= te.getHeight();
48640             te.setWidth(width);
48641         }
48642         if(this.footer){
48643             var te = this.footer.getEl();
48644             Roo.log("footer:" + te.getHeight());
48645             
48646             height -= te.getHeight();
48647             te.setWidth(width);
48648         }
48649         
48650         
48651         if(this.adjustments){
48652             width += this.adjustments[0];
48653             height += this.adjustments[1];
48654         }
48655         return {"width": width, "height": height};
48656     },
48657     
48658     setSize : function(width, height){
48659         if(this.fitToFrame && !this.ignoreResize(width, height)){
48660             if(this.fitContainer && this.resizeEl != this.el){
48661                 this.el.setSize(width, height);
48662             }
48663             var size = this.adjustForComponents(width, height);
48664             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
48665             this.fireEvent('resize', this, size.width, size.height);
48666         }
48667     },
48668     
48669     /**
48670      * Returns this panel's title
48671      * @return {String} 
48672      */
48673     getTitle : function(){
48674         return this.title;
48675     },
48676     
48677     /**
48678      * Set this panel's title
48679      * @param {String} title
48680      */
48681     setTitle : function(title){
48682         this.title = title;
48683         if(this.region){
48684             this.region.updatePanelTitle(this, title);
48685         }
48686     },
48687     
48688     /**
48689      * Returns true is this panel was configured to be closable
48690      * @return {Boolean} 
48691      */
48692     isClosable : function(){
48693         return this.closable;
48694     },
48695     
48696     beforeSlide : function(){
48697         this.el.clip();
48698         this.resizeEl.clip();
48699     },
48700     
48701     afterSlide : function(){
48702         this.el.unclip();
48703         this.resizeEl.unclip();
48704     },
48705     
48706     /**
48707      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
48708      *   Will fail silently if the {@link #setUrl} method has not been called.
48709      *   This does not activate the panel, just updates its content.
48710      */
48711     refresh : function(){
48712         if(this.refreshDelegate){
48713            this.loaded = false;
48714            this.refreshDelegate();
48715         }
48716     },
48717     
48718     /**
48719      * Destroys this panel
48720      */
48721     destroy : function(){
48722         this.el.removeAllListeners();
48723         var tempEl = document.createElement("span");
48724         tempEl.appendChild(this.el.dom);
48725         tempEl.innerHTML = "";
48726         this.el.remove();
48727         this.el = null;
48728     },
48729     
48730     /**
48731      * form - if the content panel contains a form - this is a reference to it.
48732      * @type {Roo.form.Form}
48733      */
48734     form : false,
48735     /**
48736      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
48737      *    This contains a reference to it.
48738      * @type {Roo.View}
48739      */
48740     view : false,
48741     
48742       /**
48743      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
48744      * <pre><code>
48745
48746 layout.addxtype({
48747        xtype : 'Form',
48748        items: [ .... ]
48749    }
48750 );
48751
48752 </code></pre>
48753      * @param {Object} cfg Xtype definition of item to add.
48754      */
48755     
48756     addxtype : function(cfg) {
48757         // add form..
48758         if (cfg.xtype.match(/^Form$/)) {
48759             
48760             var el;
48761             //if (this.footer) {
48762             //    el = this.footer.container.insertSibling(false, 'before');
48763             //} else {
48764                 el = this.el.createChild();
48765             //}
48766
48767             this.form = new  Roo.form.Form(cfg);
48768             
48769             
48770             if ( this.form.allItems.length) this.form.render(el.dom);
48771             return this.form;
48772         }
48773         // should only have one of theses..
48774         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
48775             // views..
48776             cfg.el = this.el.appendChild(document.createElement("div"));
48777             // factory?
48778             
48779             var ret = new Roo.factory(cfg);
48780             ret.render && ret.render(false, ''); // render blank..
48781             this.view = ret;
48782             return ret;
48783         }
48784         return false;
48785     }
48786 });
48787
48788 /**
48789  * @class Roo.GridPanel
48790  * @extends Roo.ContentPanel
48791  * @constructor
48792  * Create a new GridPanel.
48793  * @param {Roo.grid.Grid} grid The grid for this panel
48794  * @param {String/Object} config A string to set only the panel's title, or a config object
48795  */
48796 Roo.GridPanel = function(grid, config){
48797     
48798   
48799     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
48800         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
48801         
48802     this.wrapper.dom.appendChild(grid.getGridEl().dom);
48803     
48804     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
48805     
48806     if(this.toolbar){
48807         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
48808     }
48809     // xtype created footer. - not sure if will work as we normally have to render first..
48810     if (this.footer && !this.footer.el && this.footer.xtype) {
48811         
48812         this.footer.container = this.grid.getView().getFooterPanel(true);
48813         this.footer.dataSource = this.grid.dataSource;
48814         this.footer = Roo.factory(this.footer, Roo);
48815         
48816     }
48817     
48818     grid.monitorWindowResize = false; // turn off autosizing
48819     grid.autoHeight = false;
48820     grid.autoWidth = false;
48821     this.grid = grid;
48822     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
48823 };
48824
48825 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
48826     getId : function(){
48827         return this.grid.id;
48828     },
48829     
48830     /**
48831      * Returns the grid for this panel
48832      * @return {Roo.grid.Grid} 
48833      */
48834     getGrid : function(){
48835         return this.grid;    
48836     },
48837     
48838     setSize : function(width, height){
48839         if(!this.ignoreResize(width, height)){
48840             var grid = this.grid;
48841             var size = this.adjustForComponents(width, height);
48842             grid.getGridEl().setSize(size.width, size.height);
48843             grid.autoSize();
48844         }
48845     },
48846     
48847     beforeSlide : function(){
48848         this.grid.getView().scroller.clip();
48849     },
48850     
48851     afterSlide : function(){
48852         this.grid.getView().scroller.unclip();
48853     },
48854     
48855     destroy : function(){
48856         this.grid.destroy();
48857         delete this.grid;
48858         Roo.GridPanel.superclass.destroy.call(this); 
48859     }
48860 });
48861
48862
48863 /**
48864  * @class Roo.NestedLayoutPanel
48865  * @extends Roo.ContentPanel
48866  * @constructor
48867  * Create a new NestedLayoutPanel.
48868  * 
48869  * 
48870  * @param {Roo.BorderLayout} layout The layout for this panel
48871  * @param {String/Object} config A string to set only the title or a config object
48872  */
48873 Roo.NestedLayoutPanel = function(layout, config)
48874 {
48875     // construct with only one argument..
48876     /* FIXME - implement nicer consturctors
48877     if (layout.layout) {
48878         config = layout;
48879         layout = config.layout;
48880         delete config.layout;
48881     }
48882     if (layout.xtype && !layout.getEl) {
48883         // then layout needs constructing..
48884         layout = Roo.factory(layout, Roo);
48885     }
48886     */
48887     
48888     
48889     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
48890     
48891     layout.monitorWindowResize = false; // turn off autosizing
48892     this.layout = layout;
48893     this.layout.getEl().addClass("x-layout-nested-layout");
48894     
48895     
48896     
48897     
48898 };
48899
48900 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
48901
48902     setSize : function(width, height){
48903         if(!this.ignoreResize(width, height)){
48904             var size = this.adjustForComponents(width, height);
48905             var el = this.layout.getEl();
48906             el.setSize(size.width, size.height);
48907             var touch = el.dom.offsetWidth;
48908             this.layout.layout();
48909             // ie requires a double layout on the first pass
48910             if(Roo.isIE && !this.initialized){
48911                 this.initialized = true;
48912                 this.layout.layout();
48913             }
48914         }
48915     },
48916     
48917     // activate all subpanels if not currently active..
48918     
48919     setActiveState : function(active){
48920         this.active = active;
48921         if(!active){
48922             this.fireEvent("deactivate", this);
48923             return;
48924         }
48925         
48926         this.fireEvent("activate", this);
48927         // not sure if this should happen before or after..
48928         if (!this.layout) {
48929             return; // should not happen..
48930         }
48931         var reg = false;
48932         for (var r in this.layout.regions) {
48933             reg = this.layout.getRegion(r);
48934             if (reg.getActivePanel()) {
48935                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
48936                 reg.setActivePanel(reg.getActivePanel());
48937                 continue;
48938             }
48939             if (!reg.panels.length) {
48940                 continue;
48941             }
48942             reg.showPanel(reg.getPanel(0));
48943         }
48944         
48945         
48946         
48947         
48948     },
48949     
48950     /**
48951      * Returns the nested BorderLayout for this panel
48952      * @return {Roo.BorderLayout} 
48953      */
48954     getLayout : function(){
48955         return this.layout;
48956     },
48957     
48958      /**
48959      * Adds a xtype elements to the layout of the nested panel
48960      * <pre><code>
48961
48962 panel.addxtype({
48963        xtype : 'ContentPanel',
48964        region: 'west',
48965        items: [ .... ]
48966    }
48967 );
48968
48969 panel.addxtype({
48970         xtype : 'NestedLayoutPanel',
48971         region: 'west',
48972         layout: {
48973            center: { },
48974            west: { }   
48975         },
48976         items : [ ... list of content panels or nested layout panels.. ]
48977    }
48978 );
48979 </code></pre>
48980      * @param {Object} cfg Xtype definition of item to add.
48981      */
48982     addxtype : function(cfg) {
48983         return this.layout.addxtype(cfg);
48984     
48985     }
48986 });
48987
48988 Roo.ScrollPanel = function(el, config, content){
48989     config = config || {};
48990     config.fitToFrame = true;
48991     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
48992     
48993     this.el.dom.style.overflow = "hidden";
48994     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
48995     this.el.removeClass("x-layout-inactive-content");
48996     this.el.on("mousewheel", this.onWheel, this);
48997
48998     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
48999     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
49000     up.unselectable(); down.unselectable();
49001     up.on("click", this.scrollUp, this);
49002     down.on("click", this.scrollDown, this);
49003     up.addClassOnOver("x-scroller-btn-over");
49004     down.addClassOnOver("x-scroller-btn-over");
49005     up.addClassOnClick("x-scroller-btn-click");
49006     down.addClassOnClick("x-scroller-btn-click");
49007     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
49008
49009     this.resizeEl = this.el;
49010     this.el = wrap; this.up = up; this.down = down;
49011 };
49012
49013 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
49014     increment : 100,
49015     wheelIncrement : 5,
49016     scrollUp : function(){
49017         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
49018     },
49019
49020     scrollDown : function(){
49021         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
49022     },
49023
49024     afterScroll : function(){
49025         var el = this.resizeEl;
49026         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
49027         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
49028         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
49029     },
49030
49031     setSize : function(){
49032         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
49033         this.afterScroll();
49034     },
49035
49036     onWheel : function(e){
49037         var d = e.getWheelDelta();
49038         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
49039         this.afterScroll();
49040         e.stopEvent();
49041     },
49042
49043     setContent : function(content, loadScripts){
49044         this.resizeEl.update(content, loadScripts);
49045     }
49046
49047 });
49048
49049
49050
49051
49052
49053
49054
49055
49056
49057 /**
49058  * @class Roo.TreePanel
49059  * @extends Roo.ContentPanel
49060  * @constructor
49061  * Create a new TreePanel. - defaults to fit/scoll contents.
49062  * @param {String/Object} config A string to set only the panel's title, or a config object
49063  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
49064  */
49065 Roo.TreePanel = function(config){
49066     var el = config.el;
49067     var tree = config.tree;
49068     delete config.tree; 
49069     delete config.el; // hopefull!
49070     
49071     // wrapper for IE7 strict & safari scroll issue
49072     
49073     var treeEl = el.createChild();
49074     config.resizeEl = treeEl;
49075     
49076     
49077     
49078     Roo.TreePanel.superclass.constructor.call(this, el, config);
49079  
49080  
49081     this.tree = new Roo.tree.TreePanel(treeEl , tree);
49082     //console.log(tree);
49083     this.on('activate', function()
49084     {
49085         if (this.tree.rendered) {
49086             return;
49087         }
49088         //console.log('render tree');
49089         this.tree.render();
49090     });
49091     // this should not be needed.. - it's actually the 'el' that resizes?
49092     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
49093     
49094     //this.on('resize',  function (cp, w, h) {
49095     //        this.tree.innerCt.setWidth(w);
49096     //        this.tree.innerCt.setHeight(h);
49097     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
49098     //});
49099
49100         
49101     
49102 };
49103
49104 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
49105     fitToFrame : true,
49106     autoScroll : true
49107 });
49108
49109
49110
49111
49112
49113
49114
49115
49116
49117
49118
49119 /*
49120  * Based on:
49121  * Ext JS Library 1.1.1
49122  * Copyright(c) 2006-2007, Ext JS, LLC.
49123  *
49124  * Originally Released Under LGPL - original licence link has changed is not relivant.
49125  *
49126  * Fork - LGPL
49127  * <script type="text/javascript">
49128  */
49129  
49130
49131 /**
49132  * @class Roo.ReaderLayout
49133  * @extends Roo.BorderLayout
49134  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
49135  * center region containing two nested regions (a top one for a list view and one for item preview below),
49136  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
49137  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
49138  * expedites the setup of the overall layout and regions for this common application style.
49139  * Example:
49140  <pre><code>
49141 var reader = new Roo.ReaderLayout();
49142 var CP = Roo.ContentPanel;  // shortcut for adding
49143
49144 reader.beginUpdate();
49145 reader.add("north", new CP("north", "North"));
49146 reader.add("west", new CP("west", {title: "West"}));
49147 reader.add("east", new CP("east", {title: "East"}));
49148
49149 reader.regions.listView.add(new CP("listView", "List"));
49150 reader.regions.preview.add(new CP("preview", "Preview"));
49151 reader.endUpdate();
49152 </code></pre>
49153 * @constructor
49154 * Create a new ReaderLayout
49155 * @param {Object} config Configuration options
49156 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
49157 * document.body if omitted)
49158 */
49159 Roo.ReaderLayout = function(config, renderTo){
49160     var c = config || {size:{}};
49161     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
49162         north: c.north !== false ? Roo.apply({
49163             split:false,
49164             initialSize: 32,
49165             titlebar: false
49166         }, c.north) : false,
49167         west: c.west !== false ? Roo.apply({
49168             split:true,
49169             initialSize: 200,
49170             minSize: 175,
49171             maxSize: 400,
49172             titlebar: true,
49173             collapsible: true,
49174             animate: true,
49175             margins:{left:5,right:0,bottom:5,top:5},
49176             cmargins:{left:5,right:5,bottom:5,top:5}
49177         }, c.west) : false,
49178         east: c.east !== false ? Roo.apply({
49179             split:true,
49180             initialSize: 200,
49181             minSize: 175,
49182             maxSize: 400,
49183             titlebar: true,
49184             collapsible: true,
49185             animate: true,
49186             margins:{left:0,right:5,bottom:5,top:5},
49187             cmargins:{left:5,right:5,bottom:5,top:5}
49188         }, c.east) : false,
49189         center: Roo.apply({
49190             tabPosition: 'top',
49191             autoScroll:false,
49192             closeOnTab: true,
49193             titlebar:false,
49194             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
49195         }, c.center)
49196     });
49197
49198     this.el.addClass('x-reader');
49199
49200     this.beginUpdate();
49201
49202     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
49203         south: c.preview !== false ? Roo.apply({
49204             split:true,
49205             initialSize: 200,
49206             minSize: 100,
49207             autoScroll:true,
49208             collapsible:true,
49209             titlebar: true,
49210             cmargins:{top:5,left:0, right:0, bottom:0}
49211         }, c.preview) : false,
49212         center: Roo.apply({
49213             autoScroll:false,
49214             titlebar:false,
49215             minHeight:200
49216         }, c.listView)
49217     });
49218     this.add('center', new Roo.NestedLayoutPanel(inner,
49219             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
49220
49221     this.endUpdate();
49222
49223     this.regions.preview = inner.getRegion('south');
49224     this.regions.listView = inner.getRegion('center');
49225 };
49226
49227 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
49228  * Based on:
49229  * Ext JS Library 1.1.1
49230  * Copyright(c) 2006-2007, Ext JS, LLC.
49231  *
49232  * Originally Released Under LGPL - original licence link has changed is not relivant.
49233  *
49234  * Fork - LGPL
49235  * <script type="text/javascript">
49236  */
49237  
49238 /**
49239  * @class Roo.grid.Grid
49240  * @extends Roo.util.Observable
49241  * This class represents the primary interface of a component based grid control.
49242  * <br><br>Usage:<pre><code>
49243  var grid = new Roo.grid.Grid("my-container-id", {
49244      ds: myDataStore,
49245      cm: myColModel,
49246      selModel: mySelectionModel,
49247      autoSizeColumns: true,
49248      monitorWindowResize: false,
49249      trackMouseOver: true
49250  });
49251  // set any options
49252  grid.render();
49253  * </code></pre>
49254  * <b>Common Problems:</b><br/>
49255  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
49256  * element will correct this<br/>
49257  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
49258  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
49259  * are unpredictable.<br/>
49260  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
49261  * grid to calculate dimensions/offsets.<br/>
49262   * @constructor
49263  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
49264  * The container MUST have some type of size defined for the grid to fill. The container will be
49265  * automatically set to position relative if it isn't already.
49266  * @param {Object} config A config object that sets properties on this grid.
49267  */
49268 Roo.grid.Grid = function(container, config){
49269         // initialize the container
49270         this.container = Roo.get(container);
49271         this.container.update("");
49272         this.container.setStyle("overflow", "hidden");
49273     this.container.addClass('x-grid-container');
49274
49275     this.id = this.container.id;
49276
49277     Roo.apply(this, config);
49278     // check and correct shorthanded configs
49279     if(this.ds){
49280         this.dataSource = this.ds;
49281         delete this.ds;
49282     }
49283     if(this.cm){
49284         this.colModel = this.cm;
49285         delete this.cm;
49286     }
49287     if(this.sm){
49288         this.selModel = this.sm;
49289         delete this.sm;
49290     }
49291
49292     if (this.selModel) {
49293         this.selModel = Roo.factory(this.selModel, Roo.grid);
49294         this.sm = this.selModel;
49295         this.sm.xmodule = this.xmodule || false;
49296     }
49297     if (typeof(this.colModel.config) == 'undefined') {
49298         this.colModel = new Roo.grid.ColumnModel(this.colModel);
49299         this.cm = this.colModel;
49300         this.cm.xmodule = this.xmodule || false;
49301     }
49302     if (this.dataSource) {
49303         this.dataSource= Roo.factory(this.dataSource, Roo.data);
49304         this.ds = this.dataSource;
49305         this.ds.xmodule = this.xmodule || false;
49306          
49307     }
49308     
49309     
49310     
49311     if(this.width){
49312         this.container.setWidth(this.width);
49313     }
49314
49315     if(this.height){
49316         this.container.setHeight(this.height);
49317     }
49318     /** @private */
49319         this.addEvents({
49320         // raw events
49321         /**
49322          * @event click
49323          * The raw click event for the entire grid.
49324          * @param {Roo.EventObject} e
49325          */
49326         "click" : true,
49327         /**
49328          * @event dblclick
49329          * The raw dblclick event for the entire grid.
49330          * @param {Roo.EventObject} e
49331          */
49332         "dblclick" : true,
49333         /**
49334          * @event contextmenu
49335          * The raw contextmenu event for the entire grid.
49336          * @param {Roo.EventObject} e
49337          */
49338         "contextmenu" : true,
49339         /**
49340          * @event mousedown
49341          * The raw mousedown event for the entire grid.
49342          * @param {Roo.EventObject} e
49343          */
49344         "mousedown" : true,
49345         /**
49346          * @event mouseup
49347          * The raw mouseup event for the entire grid.
49348          * @param {Roo.EventObject} e
49349          */
49350         "mouseup" : true,
49351         /**
49352          * @event mouseover
49353          * The raw mouseover event for the entire grid.
49354          * @param {Roo.EventObject} e
49355          */
49356         "mouseover" : true,
49357         /**
49358          * @event mouseout
49359          * The raw mouseout event for the entire grid.
49360          * @param {Roo.EventObject} e
49361          */
49362         "mouseout" : true,
49363         /**
49364          * @event keypress
49365          * The raw keypress event for the entire grid.
49366          * @param {Roo.EventObject} e
49367          */
49368         "keypress" : true,
49369         /**
49370          * @event keydown
49371          * The raw keydown event for the entire grid.
49372          * @param {Roo.EventObject} e
49373          */
49374         "keydown" : true,
49375
49376         // custom events
49377
49378         /**
49379          * @event cellclick
49380          * Fires when a cell is clicked
49381          * @param {Grid} this
49382          * @param {Number} rowIndex
49383          * @param {Number} columnIndex
49384          * @param {Roo.EventObject} e
49385          */
49386         "cellclick" : true,
49387         /**
49388          * @event celldblclick
49389          * Fires when a cell is double clicked
49390          * @param {Grid} this
49391          * @param {Number} rowIndex
49392          * @param {Number} columnIndex
49393          * @param {Roo.EventObject} e
49394          */
49395         "celldblclick" : true,
49396         /**
49397          * @event rowclick
49398          * Fires when a row is clicked
49399          * @param {Grid} this
49400          * @param {Number} rowIndex
49401          * @param {Roo.EventObject} e
49402          */
49403         "rowclick" : true,
49404         /**
49405          * @event rowdblclick
49406          * Fires when a row is double clicked
49407          * @param {Grid} this
49408          * @param {Number} rowIndex
49409          * @param {Roo.EventObject} e
49410          */
49411         "rowdblclick" : true,
49412         /**
49413          * @event headerclick
49414          * Fires when a header is clicked
49415          * @param {Grid} this
49416          * @param {Number} columnIndex
49417          * @param {Roo.EventObject} e
49418          */
49419         "headerclick" : true,
49420         /**
49421          * @event headerdblclick
49422          * Fires when a header cell is double clicked
49423          * @param {Grid} this
49424          * @param {Number} columnIndex
49425          * @param {Roo.EventObject} e
49426          */
49427         "headerdblclick" : true,
49428         /**
49429          * @event rowcontextmenu
49430          * Fires when a row is right clicked
49431          * @param {Grid} this
49432          * @param {Number} rowIndex
49433          * @param {Roo.EventObject} e
49434          */
49435         "rowcontextmenu" : true,
49436         /**
49437          * @event cellcontextmenu
49438          * Fires when a cell is right clicked
49439          * @param {Grid} this
49440          * @param {Number} rowIndex
49441          * @param {Number} cellIndex
49442          * @param {Roo.EventObject} e
49443          */
49444          "cellcontextmenu" : true,
49445         /**
49446          * @event headercontextmenu
49447          * Fires when a header is right clicked
49448          * @param {Grid} this
49449          * @param {Number} columnIndex
49450          * @param {Roo.EventObject} e
49451          */
49452         "headercontextmenu" : true,
49453         /**
49454          * @event bodyscroll
49455          * Fires when the body element is scrolled
49456          * @param {Number} scrollLeft
49457          * @param {Number} scrollTop
49458          */
49459         "bodyscroll" : true,
49460         /**
49461          * @event columnresize
49462          * Fires when the user resizes a column
49463          * @param {Number} columnIndex
49464          * @param {Number} newSize
49465          */
49466         "columnresize" : true,
49467         /**
49468          * @event columnmove
49469          * Fires when the user moves a column
49470          * @param {Number} oldIndex
49471          * @param {Number} newIndex
49472          */
49473         "columnmove" : true,
49474         /**
49475          * @event startdrag
49476          * Fires when row(s) start being dragged
49477          * @param {Grid} this
49478          * @param {Roo.GridDD} dd The drag drop object
49479          * @param {event} e The raw browser event
49480          */
49481         "startdrag" : true,
49482         /**
49483          * @event enddrag
49484          * Fires when a drag operation is complete
49485          * @param {Grid} this
49486          * @param {Roo.GridDD} dd The drag drop object
49487          * @param {event} e The raw browser event
49488          */
49489         "enddrag" : true,
49490         /**
49491          * @event dragdrop
49492          * Fires when dragged row(s) are dropped on a valid DD target
49493          * @param {Grid} this
49494          * @param {Roo.GridDD} dd The drag drop object
49495          * @param {String} targetId The target drag drop object
49496          * @param {event} e The raw browser event
49497          */
49498         "dragdrop" : true,
49499         /**
49500          * @event dragover
49501          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
49502          * @param {Grid} this
49503          * @param {Roo.GridDD} dd The drag drop object
49504          * @param {String} targetId The target drag drop object
49505          * @param {event} e The raw browser event
49506          */
49507         "dragover" : true,
49508         /**
49509          * @event dragenter
49510          *  Fires when the dragged row(s) first cross another DD target while being dragged
49511          * @param {Grid} this
49512          * @param {Roo.GridDD} dd The drag drop object
49513          * @param {String} targetId The target drag drop object
49514          * @param {event} e The raw browser event
49515          */
49516         "dragenter" : true,
49517         /**
49518          * @event dragout
49519          * Fires when the dragged row(s) leave another DD target while being dragged
49520          * @param {Grid} this
49521          * @param {Roo.GridDD} dd The drag drop object
49522          * @param {String} targetId The target drag drop object
49523          * @param {event} e The raw browser event
49524          */
49525         "dragout" : true,
49526         /**
49527          * @event rowclass
49528          * Fires when a row is rendered, so you can change add a style to it.
49529          * @param {GridView} gridview   The grid view
49530          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
49531          */
49532         'rowclass' : true,
49533
49534         /**
49535          * @event render
49536          * Fires when the grid is rendered
49537          * @param {Grid} grid
49538          */
49539         'render' : true
49540     });
49541
49542     Roo.grid.Grid.superclass.constructor.call(this);
49543 };
49544 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
49545     
49546     /**
49547      * @cfg {String} ddGroup - drag drop group.
49548      */
49549
49550     /**
49551      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
49552      */
49553     minColumnWidth : 25,
49554
49555     /**
49556      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
49557      * <b>on initial render.</b> It is more efficient to explicitly size the columns
49558      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
49559      */
49560     autoSizeColumns : false,
49561
49562     /**
49563      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
49564      */
49565     autoSizeHeaders : true,
49566
49567     /**
49568      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
49569      */
49570     monitorWindowResize : true,
49571
49572     /**
49573      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
49574      * rows measured to get a columns size. Default is 0 (all rows).
49575      */
49576     maxRowsToMeasure : 0,
49577
49578     /**
49579      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
49580      */
49581     trackMouseOver : true,
49582
49583     /**
49584     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
49585     */
49586     
49587     /**
49588     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
49589     */
49590     enableDragDrop : false,
49591     
49592     /**
49593     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
49594     */
49595     enableColumnMove : true,
49596     
49597     /**
49598     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
49599     */
49600     enableColumnHide : true,
49601     
49602     /**
49603     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
49604     */
49605     enableRowHeightSync : false,
49606     
49607     /**
49608     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
49609     */
49610     stripeRows : true,
49611     
49612     /**
49613     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
49614     */
49615     autoHeight : false,
49616
49617     /**
49618      * @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.
49619      */
49620     autoExpandColumn : false,
49621
49622     /**
49623     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
49624     * Default is 50.
49625     */
49626     autoExpandMin : 50,
49627
49628     /**
49629     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
49630     */
49631     autoExpandMax : 1000,
49632
49633     /**
49634     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
49635     */
49636     view : null,
49637
49638     /**
49639     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
49640     */
49641     loadMask : false,
49642     /**
49643     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
49644     */
49645     dropTarget: false,
49646     
49647    
49648     
49649     // private
49650     rendered : false,
49651
49652     /**
49653     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
49654     * of a fixed width. Default is false.
49655     */
49656     /**
49657     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
49658     */
49659     /**
49660      * Called once after all setup has been completed and the grid is ready to be rendered.
49661      * @return {Roo.grid.Grid} this
49662      */
49663     render : function()
49664     {
49665         var c = this.container;
49666         // try to detect autoHeight/width mode
49667         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
49668             this.autoHeight = true;
49669         }
49670         var view = this.getView();
49671         view.init(this);
49672
49673         c.on("click", this.onClick, this);
49674         c.on("dblclick", this.onDblClick, this);
49675         c.on("contextmenu", this.onContextMenu, this);
49676         c.on("keydown", this.onKeyDown, this);
49677
49678         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
49679
49680         this.getSelectionModel().init(this);
49681
49682         view.render();
49683
49684         if(this.loadMask){
49685             this.loadMask = new Roo.LoadMask(this.container,
49686                     Roo.apply({store:this.dataSource}, this.loadMask));
49687         }
49688         
49689         
49690         if (this.toolbar && this.toolbar.xtype) {
49691             this.toolbar.container = this.getView().getHeaderPanel(true);
49692             this.toolbar = new Roo.Toolbar(this.toolbar);
49693         }
49694         if (this.footer && this.footer.xtype) {
49695             this.footer.dataSource = this.getDataSource();
49696             this.footer.container = this.getView().getFooterPanel(true);
49697             this.footer = Roo.factory(this.footer, Roo);
49698         }
49699         if (this.dropTarget && this.dropTarget.xtype) {
49700             delete this.dropTarget.xtype;
49701             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
49702         }
49703         
49704         
49705         this.rendered = true;
49706         this.fireEvent('render', this);
49707         return this;
49708     },
49709
49710         /**
49711          * Reconfigures the grid to use a different Store and Column Model.
49712          * The View will be bound to the new objects and refreshed.
49713          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
49714          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
49715          */
49716     reconfigure : function(dataSource, colModel){
49717         if(this.loadMask){
49718             this.loadMask.destroy();
49719             this.loadMask = new Roo.LoadMask(this.container,
49720                     Roo.apply({store:dataSource}, this.loadMask));
49721         }
49722         this.view.bind(dataSource, colModel);
49723         this.dataSource = dataSource;
49724         this.colModel = colModel;
49725         this.view.refresh(true);
49726     },
49727
49728     // private
49729     onKeyDown : function(e){
49730         this.fireEvent("keydown", e);
49731     },
49732
49733     /**
49734      * Destroy this grid.
49735      * @param {Boolean} removeEl True to remove the element
49736      */
49737     destroy : function(removeEl, keepListeners){
49738         if(this.loadMask){
49739             this.loadMask.destroy();
49740         }
49741         var c = this.container;
49742         c.removeAllListeners();
49743         this.view.destroy();
49744         this.colModel.purgeListeners();
49745         if(!keepListeners){
49746             this.purgeListeners();
49747         }
49748         c.update("");
49749         if(removeEl === true){
49750             c.remove();
49751         }
49752     },
49753
49754     // private
49755     processEvent : function(name, e){
49756         this.fireEvent(name, e);
49757         var t = e.getTarget();
49758         var v = this.view;
49759         var header = v.findHeaderIndex(t);
49760         if(header !== false){
49761             this.fireEvent("header" + name, this, header, e);
49762         }else{
49763             var row = v.findRowIndex(t);
49764             var cell = v.findCellIndex(t);
49765             if(row !== false){
49766                 this.fireEvent("row" + name, this, row, e);
49767                 if(cell !== false){
49768                     this.fireEvent("cell" + name, this, row, cell, e);
49769                 }
49770             }
49771         }
49772     },
49773
49774     // private
49775     onClick : function(e){
49776         this.processEvent("click", e);
49777     },
49778
49779     // private
49780     onContextMenu : function(e, t){
49781         this.processEvent("contextmenu", e);
49782     },
49783
49784     // private
49785     onDblClick : function(e){
49786         this.processEvent("dblclick", e);
49787     },
49788
49789     // private
49790     walkCells : function(row, col, step, fn, scope){
49791         var cm = this.colModel, clen = cm.getColumnCount();
49792         var ds = this.dataSource, rlen = ds.getCount(), first = true;
49793         if(step < 0){
49794             if(col < 0){
49795                 row--;
49796                 first = false;
49797             }
49798             while(row >= 0){
49799                 if(!first){
49800                     col = clen-1;
49801                 }
49802                 first = false;
49803                 while(col >= 0){
49804                     if(fn.call(scope || this, row, col, cm) === true){
49805                         return [row, col];
49806                     }
49807                     col--;
49808                 }
49809                 row--;
49810             }
49811         } else {
49812             if(col >= clen){
49813                 row++;
49814                 first = false;
49815             }
49816             while(row < rlen){
49817                 if(!first){
49818                     col = 0;
49819                 }
49820                 first = false;
49821                 while(col < clen){
49822                     if(fn.call(scope || this, row, col, cm) === true){
49823                         return [row, col];
49824                     }
49825                     col++;
49826                 }
49827                 row++;
49828             }
49829         }
49830         return null;
49831     },
49832
49833     // private
49834     getSelections : function(){
49835         return this.selModel.getSelections();
49836     },
49837
49838     /**
49839      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
49840      * but if manual update is required this method will initiate it.
49841      */
49842     autoSize : function(){
49843         if(this.rendered){
49844             this.view.layout();
49845             if(this.view.adjustForScroll){
49846                 this.view.adjustForScroll();
49847             }
49848         }
49849     },
49850
49851     /**
49852      * Returns the grid's underlying element.
49853      * @return {Element} The element
49854      */
49855     getGridEl : function(){
49856         return this.container;
49857     },
49858
49859     // private for compatibility, overridden by editor grid
49860     stopEditing : function(){},
49861
49862     /**
49863      * Returns the grid's SelectionModel.
49864      * @return {SelectionModel}
49865      */
49866     getSelectionModel : function(){
49867         if(!this.selModel){
49868             this.selModel = new Roo.grid.RowSelectionModel();
49869         }
49870         return this.selModel;
49871     },
49872
49873     /**
49874      * Returns the grid's DataSource.
49875      * @return {DataSource}
49876      */
49877     getDataSource : function(){
49878         return this.dataSource;
49879     },
49880
49881     /**
49882      * Returns the grid's ColumnModel.
49883      * @return {ColumnModel}
49884      */
49885     getColumnModel : function(){
49886         return this.colModel;
49887     },
49888
49889     /**
49890      * Returns the grid's GridView object.
49891      * @return {GridView}
49892      */
49893     getView : function(){
49894         if(!this.view){
49895             this.view = new Roo.grid.GridView(this.viewConfig);
49896         }
49897         return this.view;
49898     },
49899     /**
49900      * Called to get grid's drag proxy text, by default returns this.ddText.
49901      * @return {String}
49902      */
49903     getDragDropText : function(){
49904         var count = this.selModel.getCount();
49905         return String.format(this.ddText, count, count == 1 ? '' : 's');
49906     }
49907 });
49908 /**
49909  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
49910  * %0 is replaced with the number of selected rows.
49911  * @type String
49912  */
49913 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
49914  * Based on:
49915  * Ext JS Library 1.1.1
49916  * Copyright(c) 2006-2007, Ext JS, LLC.
49917  *
49918  * Originally Released Under LGPL - original licence link has changed is not relivant.
49919  *
49920  * Fork - LGPL
49921  * <script type="text/javascript">
49922  */
49923  
49924 Roo.grid.AbstractGridView = function(){
49925         this.grid = null;
49926         
49927         this.events = {
49928             "beforerowremoved" : true,
49929             "beforerowsinserted" : true,
49930             "beforerefresh" : true,
49931             "rowremoved" : true,
49932             "rowsinserted" : true,
49933             "rowupdated" : true,
49934             "refresh" : true
49935         };
49936     Roo.grid.AbstractGridView.superclass.constructor.call(this);
49937 };
49938
49939 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
49940     rowClass : "x-grid-row",
49941     cellClass : "x-grid-cell",
49942     tdClass : "x-grid-td",
49943     hdClass : "x-grid-hd",
49944     splitClass : "x-grid-hd-split",
49945     
49946         init: function(grid){
49947         this.grid = grid;
49948                 var cid = this.grid.getGridEl().id;
49949         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
49950         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
49951         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
49952         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
49953         },
49954         
49955         getColumnRenderers : function(){
49956         var renderers = [];
49957         var cm = this.grid.colModel;
49958         var colCount = cm.getColumnCount();
49959         for(var i = 0; i < colCount; i++){
49960             renderers[i] = cm.getRenderer(i);
49961         }
49962         return renderers;
49963     },
49964     
49965     getColumnIds : function(){
49966         var ids = [];
49967         var cm = this.grid.colModel;
49968         var colCount = cm.getColumnCount();
49969         for(var i = 0; i < colCount; i++){
49970             ids[i] = cm.getColumnId(i);
49971         }
49972         return ids;
49973     },
49974     
49975     getDataIndexes : function(){
49976         if(!this.indexMap){
49977             this.indexMap = this.buildIndexMap();
49978         }
49979         return this.indexMap.colToData;
49980     },
49981     
49982     getColumnIndexByDataIndex : function(dataIndex){
49983         if(!this.indexMap){
49984             this.indexMap = this.buildIndexMap();
49985         }
49986         return this.indexMap.dataToCol[dataIndex];
49987     },
49988     
49989     /**
49990      * Set a css style for a column dynamically. 
49991      * @param {Number} colIndex The index of the column
49992      * @param {String} name The css property name
49993      * @param {String} value The css value
49994      */
49995     setCSSStyle : function(colIndex, name, value){
49996         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
49997         Roo.util.CSS.updateRule(selector, name, value);
49998     },
49999     
50000     generateRules : function(cm){
50001         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
50002         Roo.util.CSS.removeStyleSheet(rulesId);
50003         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50004             var cid = cm.getColumnId(i);
50005             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
50006                          this.tdSelector, cid, " {\n}\n",
50007                          this.hdSelector, cid, " {\n}\n",
50008                          this.splitSelector, cid, " {\n}\n");
50009         }
50010         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
50011     }
50012 });/*
50013  * Based on:
50014  * Ext JS Library 1.1.1
50015  * Copyright(c) 2006-2007, Ext JS, LLC.
50016  *
50017  * Originally Released Under LGPL - original licence link has changed is not relivant.
50018  *
50019  * Fork - LGPL
50020  * <script type="text/javascript">
50021  */
50022
50023 // private
50024 // This is a support class used internally by the Grid components
50025 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
50026     this.grid = grid;
50027     this.view = grid.getView();
50028     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
50029     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
50030     if(hd2){
50031         this.setHandleElId(Roo.id(hd));
50032         this.setOuterHandleElId(Roo.id(hd2));
50033     }
50034     this.scroll = false;
50035 };
50036 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
50037     maxDragWidth: 120,
50038     getDragData : function(e){
50039         var t = Roo.lib.Event.getTarget(e);
50040         var h = this.view.findHeaderCell(t);
50041         if(h){
50042             return {ddel: h.firstChild, header:h};
50043         }
50044         return false;
50045     },
50046
50047     onInitDrag : function(e){
50048         this.view.headersDisabled = true;
50049         var clone = this.dragData.ddel.cloneNode(true);
50050         clone.id = Roo.id();
50051         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
50052         this.proxy.update(clone);
50053         return true;
50054     },
50055
50056     afterValidDrop : function(){
50057         var v = this.view;
50058         setTimeout(function(){
50059             v.headersDisabled = false;
50060         }, 50);
50061     },
50062
50063     afterInvalidDrop : function(){
50064         var v = this.view;
50065         setTimeout(function(){
50066             v.headersDisabled = false;
50067         }, 50);
50068     }
50069 });
50070 /*
50071  * Based on:
50072  * Ext JS Library 1.1.1
50073  * Copyright(c) 2006-2007, Ext JS, LLC.
50074  *
50075  * Originally Released Under LGPL - original licence link has changed is not relivant.
50076  *
50077  * Fork - LGPL
50078  * <script type="text/javascript">
50079  */
50080 // private
50081 // This is a support class used internally by the Grid components
50082 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
50083     this.grid = grid;
50084     this.view = grid.getView();
50085     // split the proxies so they don't interfere with mouse events
50086     this.proxyTop = Roo.DomHelper.append(document.body, {
50087         cls:"col-move-top", html:"&#160;"
50088     }, true);
50089     this.proxyBottom = Roo.DomHelper.append(document.body, {
50090         cls:"col-move-bottom", html:"&#160;"
50091     }, true);
50092     this.proxyTop.hide = this.proxyBottom.hide = function(){
50093         this.setLeftTop(-100,-100);
50094         this.setStyle("visibility", "hidden");
50095     };
50096     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
50097     // temporarily disabled
50098     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
50099     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
50100 };
50101 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
50102     proxyOffsets : [-4, -9],
50103     fly: Roo.Element.fly,
50104
50105     getTargetFromEvent : function(e){
50106         var t = Roo.lib.Event.getTarget(e);
50107         var cindex = this.view.findCellIndex(t);
50108         if(cindex !== false){
50109             return this.view.getHeaderCell(cindex);
50110         }
50111         return null;
50112     },
50113
50114     nextVisible : function(h){
50115         var v = this.view, cm = this.grid.colModel;
50116         h = h.nextSibling;
50117         while(h){
50118             if(!cm.isHidden(v.getCellIndex(h))){
50119                 return h;
50120             }
50121             h = h.nextSibling;
50122         }
50123         return null;
50124     },
50125
50126     prevVisible : function(h){
50127         var v = this.view, cm = this.grid.colModel;
50128         h = h.prevSibling;
50129         while(h){
50130             if(!cm.isHidden(v.getCellIndex(h))){
50131                 return h;
50132             }
50133             h = h.prevSibling;
50134         }
50135         return null;
50136     },
50137
50138     positionIndicator : function(h, n, e){
50139         var x = Roo.lib.Event.getPageX(e);
50140         var r = Roo.lib.Dom.getRegion(n.firstChild);
50141         var px, pt, py = r.top + this.proxyOffsets[1];
50142         if((r.right - x) <= (r.right-r.left)/2){
50143             px = r.right+this.view.borderWidth;
50144             pt = "after";
50145         }else{
50146             px = r.left;
50147             pt = "before";
50148         }
50149         var oldIndex = this.view.getCellIndex(h);
50150         var newIndex = this.view.getCellIndex(n);
50151
50152         if(this.grid.colModel.isFixed(newIndex)){
50153             return false;
50154         }
50155
50156         var locked = this.grid.colModel.isLocked(newIndex);
50157
50158         if(pt == "after"){
50159             newIndex++;
50160         }
50161         if(oldIndex < newIndex){
50162             newIndex--;
50163         }
50164         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
50165             return false;
50166         }
50167         px +=  this.proxyOffsets[0];
50168         this.proxyTop.setLeftTop(px, py);
50169         this.proxyTop.show();
50170         if(!this.bottomOffset){
50171             this.bottomOffset = this.view.mainHd.getHeight();
50172         }
50173         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
50174         this.proxyBottom.show();
50175         return pt;
50176     },
50177
50178     onNodeEnter : function(n, dd, e, data){
50179         if(data.header != n){
50180             this.positionIndicator(data.header, n, e);
50181         }
50182     },
50183
50184     onNodeOver : function(n, dd, e, data){
50185         var result = false;
50186         if(data.header != n){
50187             result = this.positionIndicator(data.header, n, e);
50188         }
50189         if(!result){
50190             this.proxyTop.hide();
50191             this.proxyBottom.hide();
50192         }
50193         return result ? this.dropAllowed : this.dropNotAllowed;
50194     },
50195
50196     onNodeOut : function(n, dd, e, data){
50197         this.proxyTop.hide();
50198         this.proxyBottom.hide();
50199     },
50200
50201     onNodeDrop : function(n, dd, e, data){
50202         var h = data.header;
50203         if(h != n){
50204             var cm = this.grid.colModel;
50205             var x = Roo.lib.Event.getPageX(e);
50206             var r = Roo.lib.Dom.getRegion(n.firstChild);
50207             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
50208             var oldIndex = this.view.getCellIndex(h);
50209             var newIndex = this.view.getCellIndex(n);
50210             var locked = cm.isLocked(newIndex);
50211             if(pt == "after"){
50212                 newIndex++;
50213             }
50214             if(oldIndex < newIndex){
50215                 newIndex--;
50216             }
50217             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
50218                 return false;
50219             }
50220             cm.setLocked(oldIndex, locked, true);
50221             cm.moveColumn(oldIndex, newIndex);
50222             this.grid.fireEvent("columnmove", oldIndex, newIndex);
50223             return true;
50224         }
50225         return false;
50226     }
50227 });
50228 /*
50229  * Based on:
50230  * Ext JS Library 1.1.1
50231  * Copyright(c) 2006-2007, Ext JS, LLC.
50232  *
50233  * Originally Released Under LGPL - original licence link has changed is not relivant.
50234  *
50235  * Fork - LGPL
50236  * <script type="text/javascript">
50237  */
50238   
50239 /**
50240  * @class Roo.grid.GridView
50241  * @extends Roo.util.Observable
50242  *
50243  * @constructor
50244  * @param {Object} config
50245  */
50246 Roo.grid.GridView = function(config){
50247     Roo.grid.GridView.superclass.constructor.call(this);
50248     this.el = null;
50249
50250     Roo.apply(this, config);
50251 };
50252
50253 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
50254
50255     unselectable :  'unselectable="on"',
50256     unselectableCls :  'x-unselectable',
50257     
50258     
50259     rowClass : "x-grid-row",
50260
50261     cellClass : "x-grid-col",
50262
50263     tdClass : "x-grid-td",
50264
50265     hdClass : "x-grid-hd",
50266
50267     splitClass : "x-grid-split",
50268
50269     sortClasses : ["sort-asc", "sort-desc"],
50270
50271     enableMoveAnim : false,
50272
50273     hlColor: "C3DAF9",
50274
50275     dh : Roo.DomHelper,
50276
50277     fly : Roo.Element.fly,
50278
50279     css : Roo.util.CSS,
50280
50281     borderWidth: 1,
50282
50283     splitOffset: 3,
50284
50285     scrollIncrement : 22,
50286
50287     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
50288
50289     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
50290
50291     bind : function(ds, cm){
50292         if(this.ds){
50293             this.ds.un("load", this.onLoad, this);
50294             this.ds.un("datachanged", this.onDataChange, this);
50295             this.ds.un("add", this.onAdd, this);
50296             this.ds.un("remove", this.onRemove, this);
50297             this.ds.un("update", this.onUpdate, this);
50298             this.ds.un("clear", this.onClear, this);
50299         }
50300         if(ds){
50301             ds.on("load", this.onLoad, this);
50302             ds.on("datachanged", this.onDataChange, this);
50303             ds.on("add", this.onAdd, this);
50304             ds.on("remove", this.onRemove, this);
50305             ds.on("update", this.onUpdate, this);
50306             ds.on("clear", this.onClear, this);
50307         }
50308         this.ds = ds;
50309
50310         if(this.cm){
50311             this.cm.un("widthchange", this.onColWidthChange, this);
50312             this.cm.un("headerchange", this.onHeaderChange, this);
50313             this.cm.un("hiddenchange", this.onHiddenChange, this);
50314             this.cm.un("columnmoved", this.onColumnMove, this);
50315             this.cm.un("columnlockchange", this.onColumnLock, this);
50316         }
50317         if(cm){
50318             this.generateRules(cm);
50319             cm.on("widthchange", this.onColWidthChange, this);
50320             cm.on("headerchange", this.onHeaderChange, this);
50321             cm.on("hiddenchange", this.onHiddenChange, this);
50322             cm.on("columnmoved", this.onColumnMove, this);
50323             cm.on("columnlockchange", this.onColumnLock, this);
50324         }
50325         this.cm = cm;
50326     },
50327
50328     init: function(grid){
50329         Roo.grid.GridView.superclass.init.call(this, grid);
50330
50331         this.bind(grid.dataSource, grid.colModel);
50332
50333         grid.on("headerclick", this.handleHeaderClick, this);
50334
50335         if(grid.trackMouseOver){
50336             grid.on("mouseover", this.onRowOver, this);
50337             grid.on("mouseout", this.onRowOut, this);
50338         }
50339         grid.cancelTextSelection = function(){};
50340         this.gridId = grid.id;
50341
50342         var tpls = this.templates || {};
50343
50344         if(!tpls.master){
50345             tpls.master = new Roo.Template(
50346                '<div class="x-grid" hidefocus="true">',
50347                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
50348                   '<div class="x-grid-topbar"></div>',
50349                   '<div class="x-grid-scroller"><div></div></div>',
50350                   '<div class="x-grid-locked">',
50351                       '<div class="x-grid-header">{lockedHeader}</div>',
50352                       '<div class="x-grid-body">{lockedBody}</div>',
50353                   "</div>",
50354                   '<div class="x-grid-viewport">',
50355                       '<div class="x-grid-header">{header}</div>',
50356                       '<div class="x-grid-body">{body}</div>',
50357                   "</div>",
50358                   '<div class="x-grid-bottombar"></div>',
50359                  
50360                   '<div class="x-grid-resize-proxy">&#160;</div>',
50361                "</div>"
50362             );
50363             tpls.master.disableformats = true;
50364         }
50365
50366         if(!tpls.header){
50367             tpls.header = new Roo.Template(
50368                '<table border="0" cellspacing="0" cellpadding="0">',
50369                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
50370                "</table>{splits}"
50371             );
50372             tpls.header.disableformats = true;
50373         }
50374         tpls.header.compile();
50375
50376         if(!tpls.hcell){
50377             tpls.hcell = new Roo.Template(
50378                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
50379                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
50380                 "</div></td>"
50381              );
50382              tpls.hcell.disableFormats = true;
50383         }
50384         tpls.hcell.compile();
50385
50386         if(!tpls.hsplit){
50387             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
50388                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
50389             tpls.hsplit.disableFormats = true;
50390         }
50391         tpls.hsplit.compile();
50392
50393         if(!tpls.body){
50394             tpls.body = new Roo.Template(
50395                '<table border="0" cellspacing="0" cellpadding="0">',
50396                "<tbody>{rows}</tbody>",
50397                "</table>"
50398             );
50399             tpls.body.disableFormats = true;
50400         }
50401         tpls.body.compile();
50402
50403         if(!tpls.row){
50404             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
50405             tpls.row.disableFormats = true;
50406         }
50407         tpls.row.compile();
50408
50409         if(!tpls.cell){
50410             tpls.cell = new Roo.Template(
50411                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
50412                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
50413                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
50414                 "</td>"
50415             );
50416             tpls.cell.disableFormats = true;
50417         }
50418         tpls.cell.compile();
50419
50420         this.templates = tpls;
50421     },
50422
50423     // remap these for backwards compat
50424     onColWidthChange : function(){
50425         this.updateColumns.apply(this, arguments);
50426     },
50427     onHeaderChange : function(){
50428         this.updateHeaders.apply(this, arguments);
50429     }, 
50430     onHiddenChange : function(){
50431         this.handleHiddenChange.apply(this, arguments);
50432     },
50433     onColumnMove : function(){
50434         this.handleColumnMove.apply(this, arguments);
50435     },
50436     onColumnLock : function(){
50437         this.handleLockChange.apply(this, arguments);
50438     },
50439
50440     onDataChange : function(){
50441         this.refresh();
50442         this.updateHeaderSortState();
50443     },
50444
50445     onClear : function(){
50446         this.refresh();
50447     },
50448
50449     onUpdate : function(ds, record){
50450         this.refreshRow(record);
50451     },
50452
50453     refreshRow : function(record){
50454         var ds = this.ds, index;
50455         if(typeof record == 'number'){
50456             index = record;
50457             record = ds.getAt(index);
50458         }else{
50459             index = ds.indexOf(record);
50460         }
50461         this.insertRows(ds, index, index, true);
50462         this.onRemove(ds, record, index+1, true);
50463         this.syncRowHeights(index, index);
50464         this.layout();
50465         this.fireEvent("rowupdated", this, index, record);
50466     },
50467
50468     onAdd : function(ds, records, index){
50469         this.insertRows(ds, index, index + (records.length-1));
50470     },
50471
50472     onRemove : function(ds, record, index, isUpdate){
50473         if(isUpdate !== true){
50474             this.fireEvent("beforerowremoved", this, index, record);
50475         }
50476         var bt = this.getBodyTable(), lt = this.getLockedTable();
50477         if(bt.rows[index]){
50478             bt.firstChild.removeChild(bt.rows[index]);
50479         }
50480         if(lt.rows[index]){
50481             lt.firstChild.removeChild(lt.rows[index]);
50482         }
50483         if(isUpdate !== true){
50484             this.stripeRows(index);
50485             this.syncRowHeights(index, index);
50486             this.layout();
50487             this.fireEvent("rowremoved", this, index, record);
50488         }
50489     },
50490
50491     onLoad : function(){
50492         this.scrollToTop();
50493     },
50494
50495     /**
50496      * Scrolls the grid to the top
50497      */
50498     scrollToTop : function(){
50499         if(this.scroller){
50500             this.scroller.dom.scrollTop = 0;
50501             this.syncScroll();
50502         }
50503     },
50504
50505     /**
50506      * Gets a panel in the header of the grid that can be used for toolbars etc.
50507      * After modifying the contents of this panel a call to grid.autoSize() may be
50508      * required to register any changes in size.
50509      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
50510      * @return Roo.Element
50511      */
50512     getHeaderPanel : function(doShow){
50513         if(doShow){
50514             this.headerPanel.show();
50515         }
50516         return this.headerPanel;
50517     },
50518
50519     /**
50520      * Gets a panel in the footer of the grid that can be used for toolbars etc.
50521      * After modifying the contents of this panel a call to grid.autoSize() may be
50522      * required to register any changes in size.
50523      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
50524      * @return Roo.Element
50525      */
50526     getFooterPanel : function(doShow){
50527         if(doShow){
50528             this.footerPanel.show();
50529         }
50530         return this.footerPanel;
50531     },
50532
50533     initElements : function(){
50534         var E = Roo.Element;
50535         var el = this.grid.getGridEl().dom.firstChild;
50536         var cs = el.childNodes;
50537
50538         this.el = new E(el);
50539         
50540          this.focusEl = new E(el.firstChild);
50541         this.focusEl.swallowEvent("click", true);
50542         
50543         this.headerPanel = new E(cs[1]);
50544         this.headerPanel.enableDisplayMode("block");
50545
50546         this.scroller = new E(cs[2]);
50547         this.scrollSizer = new E(this.scroller.dom.firstChild);
50548
50549         this.lockedWrap = new E(cs[3]);
50550         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
50551         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
50552
50553         this.mainWrap = new E(cs[4]);
50554         this.mainHd = new E(this.mainWrap.dom.firstChild);
50555         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
50556
50557         this.footerPanel = new E(cs[5]);
50558         this.footerPanel.enableDisplayMode("block");
50559
50560         this.resizeProxy = new E(cs[6]);
50561
50562         this.headerSelector = String.format(
50563            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
50564            this.lockedHd.id, this.mainHd.id
50565         );
50566
50567         this.splitterSelector = String.format(
50568            '#{0} div.x-grid-split, #{1} div.x-grid-split',
50569            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
50570         );
50571     },
50572     idToCssName : function(s)
50573     {
50574         return s.replace(/[^a-z0-9]+/ig, '-');
50575     },
50576
50577     getHeaderCell : function(index){
50578         return Roo.DomQuery.select(this.headerSelector)[index];
50579     },
50580
50581     getHeaderCellMeasure : function(index){
50582         return this.getHeaderCell(index).firstChild;
50583     },
50584
50585     getHeaderCellText : function(index){
50586         return this.getHeaderCell(index).firstChild.firstChild;
50587     },
50588
50589     getLockedTable : function(){
50590         return this.lockedBody.dom.firstChild;
50591     },
50592
50593     getBodyTable : function(){
50594         return this.mainBody.dom.firstChild;
50595     },
50596
50597     getLockedRow : function(index){
50598         return this.getLockedTable().rows[index];
50599     },
50600
50601     getRow : function(index){
50602         return this.getBodyTable().rows[index];
50603     },
50604
50605     getRowComposite : function(index){
50606         if(!this.rowEl){
50607             this.rowEl = new Roo.CompositeElementLite();
50608         }
50609         var els = [], lrow, mrow;
50610         if(lrow = this.getLockedRow(index)){
50611             els.push(lrow);
50612         }
50613         if(mrow = this.getRow(index)){
50614             els.push(mrow);
50615         }
50616         this.rowEl.elements = els;
50617         return this.rowEl;
50618     },
50619     /**
50620      * Gets the 'td' of the cell
50621      * 
50622      * @param {Integer} rowIndex row to select
50623      * @param {Integer} colIndex column to select
50624      * 
50625      * @return {Object} 
50626      */
50627     getCell : function(rowIndex, colIndex){
50628         var locked = this.cm.getLockedCount();
50629         var source;
50630         if(colIndex < locked){
50631             source = this.lockedBody.dom.firstChild;
50632         }else{
50633             source = this.mainBody.dom.firstChild;
50634             colIndex -= locked;
50635         }
50636         return source.rows[rowIndex].childNodes[colIndex];
50637     },
50638
50639     getCellText : function(rowIndex, colIndex){
50640         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
50641     },
50642
50643     getCellBox : function(cell){
50644         var b = this.fly(cell).getBox();
50645         if(Roo.isOpera){ // opera fails to report the Y
50646             b.y = cell.offsetTop + this.mainBody.getY();
50647         }
50648         return b;
50649     },
50650
50651     getCellIndex : function(cell){
50652         var id = String(cell.className).match(this.cellRE);
50653         if(id){
50654             return parseInt(id[1], 10);
50655         }
50656         return 0;
50657     },
50658
50659     findHeaderIndex : function(n){
50660         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
50661         return r ? this.getCellIndex(r) : false;
50662     },
50663
50664     findHeaderCell : function(n){
50665         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
50666         return r ? r : false;
50667     },
50668
50669     findRowIndex : function(n){
50670         if(!n){
50671             return false;
50672         }
50673         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
50674         return r ? r.rowIndex : false;
50675     },
50676
50677     findCellIndex : function(node){
50678         var stop = this.el.dom;
50679         while(node && node != stop){
50680             if(this.findRE.test(node.className)){
50681                 return this.getCellIndex(node);
50682             }
50683             node = node.parentNode;
50684         }
50685         return false;
50686     },
50687
50688     getColumnId : function(index){
50689         return this.cm.getColumnId(index);
50690     },
50691
50692     getSplitters : function()
50693     {
50694         if(this.splitterSelector){
50695            return Roo.DomQuery.select(this.splitterSelector);
50696         }else{
50697             return null;
50698       }
50699     },
50700
50701     getSplitter : function(index){
50702         return this.getSplitters()[index];
50703     },
50704
50705     onRowOver : function(e, t){
50706         var row;
50707         if((row = this.findRowIndex(t)) !== false){
50708             this.getRowComposite(row).addClass("x-grid-row-over");
50709         }
50710     },
50711
50712     onRowOut : function(e, t){
50713         var row;
50714         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
50715             this.getRowComposite(row).removeClass("x-grid-row-over");
50716         }
50717     },
50718
50719     renderHeaders : function(){
50720         var cm = this.cm;
50721         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
50722         var cb = [], lb = [], sb = [], lsb = [], p = {};
50723         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50724             p.cellId = "x-grid-hd-0-" + i;
50725             p.splitId = "x-grid-csplit-0-" + i;
50726             p.id = cm.getColumnId(i);
50727             p.title = cm.getColumnTooltip(i) || "";
50728             p.value = cm.getColumnHeader(i) || "";
50729             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
50730             if(!cm.isLocked(i)){
50731                 cb[cb.length] = ct.apply(p);
50732                 sb[sb.length] = st.apply(p);
50733             }else{
50734                 lb[lb.length] = ct.apply(p);
50735                 lsb[lsb.length] = st.apply(p);
50736             }
50737         }
50738         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
50739                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
50740     },
50741
50742     updateHeaders : function(){
50743         var html = this.renderHeaders();
50744         this.lockedHd.update(html[0]);
50745         this.mainHd.update(html[1]);
50746     },
50747
50748     /**
50749      * Focuses the specified row.
50750      * @param {Number} row The row index
50751      */
50752     focusRow : function(row)
50753     {
50754         //Roo.log('GridView.focusRow');
50755         var x = this.scroller.dom.scrollLeft;
50756         this.focusCell(row, 0, false);
50757         this.scroller.dom.scrollLeft = x;
50758     },
50759
50760     /**
50761      * Focuses the specified cell.
50762      * @param {Number} row The row index
50763      * @param {Number} col The column index
50764      * @param {Boolean} hscroll false to disable horizontal scrolling
50765      */
50766     focusCell : function(row, col, hscroll)
50767     {
50768         //Roo.log('GridView.focusCell');
50769         var el = this.ensureVisible(row, col, hscroll);
50770         this.focusEl.alignTo(el, "tl-tl");
50771         if(Roo.isGecko){
50772             this.focusEl.focus();
50773         }else{
50774             this.focusEl.focus.defer(1, this.focusEl);
50775         }
50776     },
50777
50778     /**
50779      * Scrolls the specified cell into view
50780      * @param {Number} row The row index
50781      * @param {Number} col The column index
50782      * @param {Boolean} hscroll false to disable horizontal scrolling
50783      */
50784     ensureVisible : function(row, col, hscroll)
50785     {
50786         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
50787         //return null; //disable for testing.
50788         if(typeof row != "number"){
50789             row = row.rowIndex;
50790         }
50791         if(row < 0 && row >= this.ds.getCount()){
50792             return  null;
50793         }
50794         col = (col !== undefined ? col : 0);
50795         var cm = this.grid.colModel;
50796         while(cm.isHidden(col)){
50797             col++;
50798         }
50799
50800         var el = this.getCell(row, col);
50801         if(!el){
50802             return null;
50803         }
50804         var c = this.scroller.dom;
50805
50806         var ctop = parseInt(el.offsetTop, 10);
50807         var cleft = parseInt(el.offsetLeft, 10);
50808         var cbot = ctop + el.offsetHeight;
50809         var cright = cleft + el.offsetWidth;
50810         
50811         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
50812         var stop = parseInt(c.scrollTop, 10);
50813         var sleft = parseInt(c.scrollLeft, 10);
50814         var sbot = stop + ch;
50815         var sright = sleft + c.clientWidth;
50816         /*
50817         Roo.log('GridView.ensureVisible:' +
50818                 ' ctop:' + ctop +
50819                 ' c.clientHeight:' + c.clientHeight +
50820                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
50821                 ' stop:' + stop +
50822                 ' cbot:' + cbot +
50823                 ' sbot:' + sbot +
50824                 ' ch:' + ch  
50825                 );
50826         */
50827         if(ctop < stop){
50828              c.scrollTop = ctop;
50829             //Roo.log("set scrolltop to ctop DISABLE?");
50830         }else if(cbot > sbot){
50831             //Roo.log("set scrolltop to cbot-ch");
50832             c.scrollTop = cbot-ch;
50833         }
50834         
50835         if(hscroll !== false){
50836             if(cleft < sleft){
50837                 c.scrollLeft = cleft;
50838             }else if(cright > sright){
50839                 c.scrollLeft = cright-c.clientWidth;
50840             }
50841         }
50842          
50843         return el;
50844     },
50845
50846     updateColumns : function(){
50847         this.grid.stopEditing();
50848         var cm = this.grid.colModel, colIds = this.getColumnIds();
50849         //var totalWidth = cm.getTotalWidth();
50850         var pos = 0;
50851         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50852             //if(cm.isHidden(i)) continue;
50853             var w = cm.getColumnWidth(i);
50854             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
50855             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
50856         }
50857         this.updateSplitters();
50858     },
50859
50860     generateRules : function(cm){
50861         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
50862         Roo.util.CSS.removeStyleSheet(rulesId);
50863         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50864             var cid = cm.getColumnId(i);
50865             var align = '';
50866             if(cm.config[i].align){
50867                 align = 'text-align:'+cm.config[i].align+';';
50868             }
50869             var hidden = '';
50870             if(cm.isHidden(i)){
50871                 hidden = 'display:none;';
50872             }
50873             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
50874             ruleBuf.push(
50875                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
50876                     this.hdSelector, cid, " {\n", align, width, "}\n",
50877                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
50878                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
50879         }
50880         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
50881     },
50882
50883     updateSplitters : function(){
50884         var cm = this.cm, s = this.getSplitters();
50885         if(s){ // splitters not created yet
50886             var pos = 0, locked = true;
50887             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50888                 if(cm.isHidden(i)) continue;
50889                 var w = cm.getColumnWidth(i); // make sure it's a number
50890                 if(!cm.isLocked(i) && locked){
50891                     pos = 0;
50892                     locked = false;
50893                 }
50894                 pos += w;
50895                 s[i].style.left = (pos-this.splitOffset) + "px";
50896             }
50897         }
50898     },
50899
50900     handleHiddenChange : function(colModel, colIndex, hidden){
50901         if(hidden){
50902             this.hideColumn(colIndex);
50903         }else{
50904             this.unhideColumn(colIndex);
50905         }
50906     },
50907
50908     hideColumn : function(colIndex){
50909         var cid = this.getColumnId(colIndex);
50910         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
50911         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
50912         if(Roo.isSafari){
50913             this.updateHeaders();
50914         }
50915         this.updateSplitters();
50916         this.layout();
50917     },
50918
50919     unhideColumn : function(colIndex){
50920         var cid = this.getColumnId(colIndex);
50921         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
50922         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
50923
50924         if(Roo.isSafari){
50925             this.updateHeaders();
50926         }
50927         this.updateSplitters();
50928         this.layout();
50929     },
50930
50931     insertRows : function(dm, firstRow, lastRow, isUpdate){
50932         if(firstRow == 0 && lastRow == dm.getCount()-1){
50933             this.refresh();
50934         }else{
50935             if(!isUpdate){
50936                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
50937             }
50938             var s = this.getScrollState();
50939             var markup = this.renderRows(firstRow, lastRow);
50940             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
50941             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
50942             this.restoreScroll(s);
50943             if(!isUpdate){
50944                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
50945                 this.syncRowHeights(firstRow, lastRow);
50946                 this.stripeRows(firstRow);
50947                 this.layout();
50948             }
50949         }
50950     },
50951
50952     bufferRows : function(markup, target, index){
50953         var before = null, trows = target.rows, tbody = target.tBodies[0];
50954         if(index < trows.length){
50955             before = trows[index];
50956         }
50957         var b = document.createElement("div");
50958         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
50959         var rows = b.firstChild.rows;
50960         for(var i = 0, len = rows.length; i < len; i++){
50961             if(before){
50962                 tbody.insertBefore(rows[0], before);
50963             }else{
50964                 tbody.appendChild(rows[0]);
50965             }
50966         }
50967         b.innerHTML = "";
50968         b = null;
50969     },
50970
50971     deleteRows : function(dm, firstRow, lastRow){
50972         if(dm.getRowCount()<1){
50973             this.fireEvent("beforerefresh", this);
50974             this.mainBody.update("");
50975             this.lockedBody.update("");
50976             this.fireEvent("refresh", this);
50977         }else{
50978             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
50979             var bt = this.getBodyTable();
50980             var tbody = bt.firstChild;
50981             var rows = bt.rows;
50982             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
50983                 tbody.removeChild(rows[firstRow]);
50984             }
50985             this.stripeRows(firstRow);
50986             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
50987         }
50988     },
50989
50990     updateRows : function(dataSource, firstRow, lastRow){
50991         var s = this.getScrollState();
50992         this.refresh();
50993         this.restoreScroll(s);
50994     },
50995
50996     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
50997         if(!noRefresh){
50998            this.refresh();
50999         }
51000         this.updateHeaderSortState();
51001     },
51002
51003     getScrollState : function(){
51004         
51005         var sb = this.scroller.dom;
51006         return {left: sb.scrollLeft, top: sb.scrollTop};
51007     },
51008
51009     stripeRows : function(startRow){
51010         if(!this.grid.stripeRows || this.ds.getCount() < 1){
51011             return;
51012         }
51013         startRow = startRow || 0;
51014         var rows = this.getBodyTable().rows;
51015         var lrows = this.getLockedTable().rows;
51016         var cls = ' x-grid-row-alt ';
51017         for(var i = startRow, len = rows.length; i < len; i++){
51018             var row = rows[i], lrow = lrows[i];
51019             var isAlt = ((i+1) % 2 == 0);
51020             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
51021             if(isAlt == hasAlt){
51022                 continue;
51023             }
51024             if(isAlt){
51025                 row.className += " x-grid-row-alt";
51026             }else{
51027                 row.className = row.className.replace("x-grid-row-alt", "");
51028             }
51029             if(lrow){
51030                 lrow.className = row.className;
51031             }
51032         }
51033     },
51034
51035     restoreScroll : function(state){
51036         //Roo.log('GridView.restoreScroll');
51037         var sb = this.scroller.dom;
51038         sb.scrollLeft = state.left;
51039         sb.scrollTop = state.top;
51040         this.syncScroll();
51041     },
51042
51043     syncScroll : function(){
51044         //Roo.log('GridView.syncScroll');
51045         var sb = this.scroller.dom;
51046         var sh = this.mainHd.dom;
51047         var bs = this.mainBody.dom;
51048         var lv = this.lockedBody.dom;
51049         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
51050         lv.scrollTop = bs.scrollTop = sb.scrollTop;
51051     },
51052
51053     handleScroll : function(e){
51054         this.syncScroll();
51055         var sb = this.scroller.dom;
51056         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
51057         e.stopEvent();
51058     },
51059
51060     handleWheel : function(e){
51061         var d = e.getWheelDelta();
51062         this.scroller.dom.scrollTop -= d*22;
51063         // set this here to prevent jumpy scrolling on large tables
51064         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
51065         e.stopEvent();
51066     },
51067
51068     renderRows : function(startRow, endRow){
51069         // pull in all the crap needed to render rows
51070         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
51071         var colCount = cm.getColumnCount();
51072
51073         if(ds.getCount() < 1){
51074             return ["", ""];
51075         }
51076
51077         // build a map for all the columns
51078         var cs = [];
51079         for(var i = 0; i < colCount; i++){
51080             var name = cm.getDataIndex(i);
51081             cs[i] = {
51082                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
51083                 renderer : cm.getRenderer(i),
51084                 id : cm.getColumnId(i),
51085                 locked : cm.isLocked(i)
51086             };
51087         }
51088
51089         startRow = startRow || 0;
51090         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
51091
51092         // records to render
51093         var rs = ds.getRange(startRow, endRow);
51094
51095         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
51096     },
51097
51098     // As much as I hate to duplicate code, this was branched because FireFox really hates
51099     // [].join("") on strings. The performance difference was substantial enough to
51100     // branch this function
51101     doRender : Roo.isGecko ?
51102             function(cs, rs, ds, startRow, colCount, stripe){
51103                 var ts = this.templates, ct = ts.cell, rt = ts.row;
51104                 // buffers
51105                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
51106                 
51107                 var hasListener = this.grid.hasListener('rowclass');
51108                 var rowcfg = {};
51109                 for(var j = 0, len = rs.length; j < len; j++){
51110                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
51111                     for(var i = 0; i < colCount; i++){
51112                         c = cs[i];
51113                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
51114                         p.id = c.id;
51115                         p.css = p.attr = "";
51116                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
51117                         if(p.value == undefined || p.value === "") p.value = "&#160;";
51118                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
51119                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
51120                         }
51121                         var markup = ct.apply(p);
51122                         if(!c.locked){
51123                             cb+= markup;
51124                         }else{
51125                             lcb+= markup;
51126                         }
51127                     }
51128                     var alt = [];
51129                     if(stripe && ((rowIndex+1) % 2 == 0)){
51130                         alt.push("x-grid-row-alt")
51131                     }
51132                     if(r.dirty){
51133                         alt.push(  " x-grid-dirty-row");
51134                     }
51135                     rp.cells = lcb;
51136                     if(this.getRowClass){
51137                         alt.push(this.getRowClass(r, rowIndex));
51138                     }
51139                     if (hasListener) {
51140                         rowcfg = {
51141                              
51142                             record: r,
51143                             rowIndex : rowIndex,
51144                             rowClass : ''
51145                         }
51146                         this.grid.fireEvent('rowclass', this, rowcfg);
51147                         alt.push(rowcfg.rowClass);
51148                     }
51149                     rp.alt = alt.join(" ");
51150                     lbuf+= rt.apply(rp);
51151                     rp.cells = cb;
51152                     buf+=  rt.apply(rp);
51153                 }
51154                 return [lbuf, buf];
51155             } :
51156             function(cs, rs, ds, startRow, colCount, stripe){
51157                 var ts = this.templates, ct = ts.cell, rt = ts.row;
51158                 // buffers
51159                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
51160                 var hasListener = this.grid.hasListener('rowclass');
51161  
51162                 var rowcfg = {};
51163                 for(var j = 0, len = rs.length; j < len; j++){
51164                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
51165                     for(var i = 0; i < colCount; i++){
51166                         c = cs[i];
51167                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
51168                         p.id = c.id;
51169                         p.css = p.attr = "";
51170                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
51171                         if(p.value == undefined || p.value === "") p.value = "&#160;";
51172                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
51173                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
51174                         }
51175                         
51176                         var markup = ct.apply(p);
51177                         if(!c.locked){
51178                             cb[cb.length] = markup;
51179                         }else{
51180                             lcb[lcb.length] = markup;
51181                         }
51182                     }
51183                     var alt = [];
51184                     if(stripe && ((rowIndex+1) % 2 == 0)){
51185                         alt.push( "x-grid-row-alt");
51186                     }
51187                     if(r.dirty){
51188                         alt.push(" x-grid-dirty-row");
51189                     }
51190                     rp.cells = lcb;
51191                     if(this.getRowClass){
51192                         alt.push( this.getRowClass(r, rowIndex));
51193                     }
51194                     if (hasListener) {
51195                         rowcfg = {
51196                              
51197                             record: r,
51198                             rowIndex : rowIndex,
51199                             rowClass : ''
51200                         }
51201                         this.grid.fireEvent('rowclass', this, rowcfg);
51202                         alt.push(rowcfg.rowClass);
51203                     }
51204                     rp.alt = alt.join(" ");
51205                     rp.cells = lcb.join("");
51206                     lbuf[lbuf.length] = rt.apply(rp);
51207                     rp.cells = cb.join("");
51208                     buf[buf.length] =  rt.apply(rp);
51209                 }
51210                 return [lbuf.join(""), buf.join("")];
51211             },
51212
51213     renderBody : function(){
51214         var markup = this.renderRows();
51215         var bt = this.templates.body;
51216         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
51217     },
51218
51219     /**
51220      * Refreshes the grid
51221      * @param {Boolean} headersToo
51222      */
51223     refresh : function(headersToo){
51224         this.fireEvent("beforerefresh", this);
51225         this.grid.stopEditing();
51226         var result = this.renderBody();
51227         this.lockedBody.update(result[0]);
51228         this.mainBody.update(result[1]);
51229         if(headersToo === true){
51230             this.updateHeaders();
51231             this.updateColumns();
51232             this.updateSplitters();
51233             this.updateHeaderSortState();
51234         }
51235         this.syncRowHeights();
51236         this.layout();
51237         this.fireEvent("refresh", this);
51238     },
51239
51240     handleColumnMove : function(cm, oldIndex, newIndex){
51241         this.indexMap = null;
51242         var s = this.getScrollState();
51243         this.refresh(true);
51244         this.restoreScroll(s);
51245         this.afterMove(newIndex);
51246     },
51247
51248     afterMove : function(colIndex){
51249         if(this.enableMoveAnim && Roo.enableFx){
51250             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
51251         }
51252         // if multisort - fix sortOrder, and reload..
51253         if (this.grid.dataSource.multiSort) {
51254             // the we can call sort again..
51255             var dm = this.grid.dataSource;
51256             var cm = this.grid.colModel;
51257             var so = [];
51258             for(var i = 0; i < cm.config.length; i++ ) {
51259                 
51260                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
51261                     continue; // dont' bother, it's not in sort list or being set.
51262                 }
51263                 
51264                 so.push(cm.config[i].dataIndex);
51265             };
51266             dm.sortOrder = so;
51267             dm.load(dm.lastOptions);
51268             
51269             
51270         }
51271         
51272     },
51273
51274     updateCell : function(dm, rowIndex, dataIndex){
51275         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
51276         if(typeof colIndex == "undefined"){ // not present in grid
51277             return;
51278         }
51279         var cm = this.grid.colModel;
51280         var cell = this.getCell(rowIndex, colIndex);
51281         var cellText = this.getCellText(rowIndex, colIndex);
51282
51283         var p = {
51284             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
51285             id : cm.getColumnId(colIndex),
51286             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
51287         };
51288         var renderer = cm.getRenderer(colIndex);
51289         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
51290         if(typeof val == "undefined" || val === "") val = "&#160;";
51291         cellText.innerHTML = val;
51292         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
51293         this.syncRowHeights(rowIndex, rowIndex);
51294     },
51295
51296     calcColumnWidth : function(colIndex, maxRowsToMeasure){
51297         var maxWidth = 0;
51298         if(this.grid.autoSizeHeaders){
51299             var h = this.getHeaderCellMeasure(colIndex);
51300             maxWidth = Math.max(maxWidth, h.scrollWidth);
51301         }
51302         var tb, index;
51303         if(this.cm.isLocked(colIndex)){
51304             tb = this.getLockedTable();
51305             index = colIndex;
51306         }else{
51307             tb = this.getBodyTable();
51308             index = colIndex - this.cm.getLockedCount();
51309         }
51310         if(tb && tb.rows){
51311             var rows = tb.rows;
51312             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
51313             for(var i = 0; i < stopIndex; i++){
51314                 var cell = rows[i].childNodes[index].firstChild;
51315                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
51316             }
51317         }
51318         return maxWidth + /*margin for error in IE*/ 5;
51319     },
51320     /**
51321      * Autofit a column to its content.
51322      * @param {Number} colIndex
51323      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
51324      */
51325      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
51326          if(this.cm.isHidden(colIndex)){
51327              return; // can't calc a hidden column
51328          }
51329         if(forceMinSize){
51330             var cid = this.cm.getColumnId(colIndex);
51331             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
51332            if(this.grid.autoSizeHeaders){
51333                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
51334            }
51335         }
51336         var newWidth = this.calcColumnWidth(colIndex);
51337         this.cm.setColumnWidth(colIndex,
51338             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
51339         if(!suppressEvent){
51340             this.grid.fireEvent("columnresize", colIndex, newWidth);
51341         }
51342     },
51343
51344     /**
51345      * Autofits all columns to their content and then expands to fit any extra space in the grid
51346      */
51347      autoSizeColumns : function(){
51348         var cm = this.grid.colModel;
51349         var colCount = cm.getColumnCount();
51350         for(var i = 0; i < colCount; i++){
51351             this.autoSizeColumn(i, true, true);
51352         }
51353         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
51354             this.fitColumns();
51355         }else{
51356             this.updateColumns();
51357             this.layout();
51358         }
51359     },
51360
51361     /**
51362      * Autofits all columns to the grid's width proportionate with their current size
51363      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
51364      */
51365     fitColumns : function(reserveScrollSpace){
51366         var cm = this.grid.colModel;
51367         var colCount = cm.getColumnCount();
51368         var cols = [];
51369         var width = 0;
51370         var i, w;
51371         for (i = 0; i < colCount; i++){
51372             if(!cm.isHidden(i) && !cm.isFixed(i)){
51373                 w = cm.getColumnWidth(i);
51374                 cols.push(i);
51375                 cols.push(w);
51376                 width += w;
51377             }
51378         }
51379         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
51380         if(reserveScrollSpace){
51381             avail -= 17;
51382         }
51383         var frac = (avail - cm.getTotalWidth())/width;
51384         while (cols.length){
51385             w = cols.pop();
51386             i = cols.pop();
51387             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
51388         }
51389         this.updateColumns();
51390         this.layout();
51391     },
51392
51393     onRowSelect : function(rowIndex){
51394         var row = this.getRowComposite(rowIndex);
51395         row.addClass("x-grid-row-selected");
51396     },
51397
51398     onRowDeselect : function(rowIndex){
51399         var row = this.getRowComposite(rowIndex);
51400         row.removeClass("x-grid-row-selected");
51401     },
51402
51403     onCellSelect : function(row, col){
51404         var cell = this.getCell(row, col);
51405         if(cell){
51406             Roo.fly(cell).addClass("x-grid-cell-selected");
51407         }
51408     },
51409
51410     onCellDeselect : function(row, col){
51411         var cell = this.getCell(row, col);
51412         if(cell){
51413             Roo.fly(cell).removeClass("x-grid-cell-selected");
51414         }
51415     },
51416
51417     updateHeaderSortState : function(){
51418         
51419         // sort state can be single { field: xxx, direction : yyy}
51420         // or   { xxx=>ASC , yyy : DESC ..... }
51421         
51422         var mstate = {};
51423         if (!this.ds.multiSort) { 
51424             var state = this.ds.getSortState();
51425             if(!state){
51426                 return;
51427             }
51428             mstate[state.field] = state.direction;
51429             // FIXME... - this is not used here.. but might be elsewhere..
51430             this.sortState = state;
51431             
51432         } else {
51433             mstate = this.ds.sortToggle;
51434         }
51435         //remove existing sort classes..
51436         
51437         var sc = this.sortClasses;
51438         var hds = this.el.select(this.headerSelector).removeClass(sc);
51439         
51440         for(var f in mstate) {
51441         
51442             var sortColumn = this.cm.findColumnIndex(f);
51443             
51444             if(sortColumn != -1){
51445                 var sortDir = mstate[f];        
51446                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
51447             }
51448         }
51449         
51450          
51451         
51452     },
51453
51454
51455     handleHeaderClick : function(g, index){
51456         if(this.headersDisabled){
51457             return;
51458         }
51459         var dm = g.dataSource, cm = g.colModel;
51460         if(!cm.isSortable(index)){
51461             return;
51462         }
51463         g.stopEditing();
51464         
51465         if (dm.multiSort) {
51466             // update the sortOrder
51467             var so = [];
51468             for(var i = 0; i < cm.config.length; i++ ) {
51469                 
51470                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
51471                     continue; // dont' bother, it's not in sort list or being set.
51472                 }
51473                 
51474                 so.push(cm.config[i].dataIndex);
51475             };
51476             dm.sortOrder = so;
51477         }
51478         
51479         
51480         dm.sort(cm.getDataIndex(index));
51481     },
51482
51483
51484     destroy : function(){
51485         if(this.colMenu){
51486             this.colMenu.removeAll();
51487             Roo.menu.MenuMgr.unregister(this.colMenu);
51488             this.colMenu.getEl().remove();
51489             delete this.colMenu;
51490         }
51491         if(this.hmenu){
51492             this.hmenu.removeAll();
51493             Roo.menu.MenuMgr.unregister(this.hmenu);
51494             this.hmenu.getEl().remove();
51495             delete this.hmenu;
51496         }
51497         if(this.grid.enableColumnMove){
51498             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
51499             if(dds){
51500                 for(var dd in dds){
51501                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
51502                         var elid = dds[dd].dragElId;
51503                         dds[dd].unreg();
51504                         Roo.get(elid).remove();
51505                     } else if(dds[dd].config.isTarget){
51506                         dds[dd].proxyTop.remove();
51507                         dds[dd].proxyBottom.remove();
51508                         dds[dd].unreg();
51509                     }
51510                     if(Roo.dd.DDM.locationCache[dd]){
51511                         delete Roo.dd.DDM.locationCache[dd];
51512                     }
51513                 }
51514                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
51515             }
51516         }
51517         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
51518         this.bind(null, null);
51519         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
51520     },
51521
51522     handleLockChange : function(){
51523         this.refresh(true);
51524     },
51525
51526     onDenyColumnLock : function(){
51527
51528     },
51529
51530     onDenyColumnHide : function(){
51531
51532     },
51533
51534     handleHdMenuClick : function(item){
51535         var index = this.hdCtxIndex;
51536         var cm = this.cm, ds = this.ds;
51537         switch(item.id){
51538             case "asc":
51539                 ds.sort(cm.getDataIndex(index), "ASC");
51540                 break;
51541             case "desc":
51542                 ds.sort(cm.getDataIndex(index), "DESC");
51543                 break;
51544             case "lock":
51545                 var lc = cm.getLockedCount();
51546                 if(cm.getColumnCount(true) <= lc+1){
51547                     this.onDenyColumnLock();
51548                     return;
51549                 }
51550                 if(lc != index){
51551                     cm.setLocked(index, true, true);
51552                     cm.moveColumn(index, lc);
51553                     this.grid.fireEvent("columnmove", index, lc);
51554                 }else{
51555                     cm.setLocked(index, true);
51556                 }
51557             break;
51558             case "unlock":
51559                 var lc = cm.getLockedCount();
51560                 if((lc-1) != index){
51561                     cm.setLocked(index, false, true);
51562                     cm.moveColumn(index, lc-1);
51563                     this.grid.fireEvent("columnmove", index, lc-1);
51564                 }else{
51565                     cm.setLocked(index, false);
51566                 }
51567             break;
51568             default:
51569                 index = cm.getIndexById(item.id.substr(4));
51570                 if(index != -1){
51571                     if(item.checked && cm.getColumnCount(true) <= 1){
51572                         this.onDenyColumnHide();
51573                         return false;
51574                     }
51575                     cm.setHidden(index, item.checked);
51576                 }
51577         }
51578         return true;
51579     },
51580
51581     beforeColMenuShow : function(){
51582         var cm = this.cm,  colCount = cm.getColumnCount();
51583         this.colMenu.removeAll();
51584         for(var i = 0; i < colCount; i++){
51585             this.colMenu.add(new Roo.menu.CheckItem({
51586                 id: "col-"+cm.getColumnId(i),
51587                 text: cm.getColumnHeader(i),
51588                 checked: !cm.isHidden(i),
51589                 hideOnClick:false
51590             }));
51591         }
51592     },
51593
51594     handleHdCtx : function(g, index, e){
51595         e.stopEvent();
51596         var hd = this.getHeaderCell(index);
51597         this.hdCtxIndex = index;
51598         var ms = this.hmenu.items, cm = this.cm;
51599         ms.get("asc").setDisabled(!cm.isSortable(index));
51600         ms.get("desc").setDisabled(!cm.isSortable(index));
51601         if(this.grid.enableColLock !== false){
51602             ms.get("lock").setDisabled(cm.isLocked(index));
51603             ms.get("unlock").setDisabled(!cm.isLocked(index));
51604         }
51605         this.hmenu.show(hd, "tl-bl");
51606     },
51607
51608     handleHdOver : function(e){
51609         var hd = this.findHeaderCell(e.getTarget());
51610         if(hd && !this.headersDisabled){
51611             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
51612                this.fly(hd).addClass("x-grid-hd-over");
51613             }
51614         }
51615     },
51616
51617     handleHdOut : function(e){
51618         var hd = this.findHeaderCell(e.getTarget());
51619         if(hd){
51620             this.fly(hd).removeClass("x-grid-hd-over");
51621         }
51622     },
51623
51624     handleSplitDblClick : function(e, t){
51625         var i = this.getCellIndex(t);
51626         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
51627             this.autoSizeColumn(i, true);
51628             this.layout();
51629         }
51630     },
51631
51632     render : function(){
51633
51634         var cm = this.cm;
51635         var colCount = cm.getColumnCount();
51636
51637         if(this.grid.monitorWindowResize === true){
51638             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51639         }
51640         var header = this.renderHeaders();
51641         var body = this.templates.body.apply({rows:""});
51642         var html = this.templates.master.apply({
51643             lockedBody: body,
51644             body: body,
51645             lockedHeader: header[0],
51646             header: header[1]
51647         });
51648
51649         //this.updateColumns();
51650
51651         this.grid.getGridEl().dom.innerHTML = html;
51652
51653         this.initElements();
51654         
51655         // a kludge to fix the random scolling effect in webkit
51656         this.el.on("scroll", function() {
51657             this.el.dom.scrollTop=0; // hopefully not recursive..
51658         },this);
51659
51660         this.scroller.on("scroll", this.handleScroll, this);
51661         this.lockedBody.on("mousewheel", this.handleWheel, this);
51662         this.mainBody.on("mousewheel", this.handleWheel, this);
51663
51664         this.mainHd.on("mouseover", this.handleHdOver, this);
51665         this.mainHd.on("mouseout", this.handleHdOut, this);
51666         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
51667                 {delegate: "."+this.splitClass});
51668
51669         this.lockedHd.on("mouseover", this.handleHdOver, this);
51670         this.lockedHd.on("mouseout", this.handleHdOut, this);
51671         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
51672                 {delegate: "."+this.splitClass});
51673
51674         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
51675             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
51676         }
51677
51678         this.updateSplitters();
51679
51680         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
51681             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
51682             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
51683         }
51684
51685         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
51686             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
51687             this.hmenu.add(
51688                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
51689                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
51690             );
51691             if(this.grid.enableColLock !== false){
51692                 this.hmenu.add('-',
51693                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
51694                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
51695                 );
51696             }
51697             if(this.grid.enableColumnHide !== false){
51698
51699                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
51700                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
51701                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
51702
51703                 this.hmenu.add('-',
51704                     {id:"columns", text: this.columnsText, menu: this.colMenu}
51705                 );
51706             }
51707             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
51708
51709             this.grid.on("headercontextmenu", this.handleHdCtx, this);
51710         }
51711
51712         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
51713             this.dd = new Roo.grid.GridDragZone(this.grid, {
51714                 ddGroup : this.grid.ddGroup || 'GridDD'
51715             });
51716             
51717         }
51718
51719         /*
51720         for(var i = 0; i < colCount; i++){
51721             if(cm.isHidden(i)){
51722                 this.hideColumn(i);
51723             }
51724             if(cm.config[i].align){
51725                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
51726                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
51727             }
51728         }*/
51729         
51730         this.updateHeaderSortState();
51731
51732         this.beforeInitialResize();
51733         this.layout(true);
51734
51735         // two part rendering gives faster view to the user
51736         this.renderPhase2.defer(1, this);
51737     },
51738
51739     renderPhase2 : function(){
51740         // render the rows now
51741         this.refresh();
51742         if(this.grid.autoSizeColumns){
51743             this.autoSizeColumns();
51744         }
51745     },
51746
51747     beforeInitialResize : function(){
51748
51749     },
51750
51751     onColumnSplitterMoved : function(i, w){
51752         this.userResized = true;
51753         var cm = this.grid.colModel;
51754         cm.setColumnWidth(i, w, true);
51755         var cid = cm.getColumnId(i);
51756         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
51757         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
51758         this.updateSplitters();
51759         this.layout();
51760         this.grid.fireEvent("columnresize", i, w);
51761     },
51762
51763     syncRowHeights : function(startIndex, endIndex){
51764         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
51765             startIndex = startIndex || 0;
51766             var mrows = this.getBodyTable().rows;
51767             var lrows = this.getLockedTable().rows;
51768             var len = mrows.length-1;
51769             endIndex = Math.min(endIndex || len, len);
51770             for(var i = startIndex; i <= endIndex; i++){
51771                 var m = mrows[i], l = lrows[i];
51772                 var h = Math.max(m.offsetHeight, l.offsetHeight);
51773                 m.style.height = l.style.height = h + "px";
51774             }
51775         }
51776     },
51777
51778     layout : function(initialRender, is2ndPass){
51779         var g = this.grid;
51780         var auto = g.autoHeight;
51781         var scrollOffset = 16;
51782         var c = g.getGridEl(), cm = this.cm,
51783                 expandCol = g.autoExpandColumn,
51784                 gv = this;
51785         //c.beginMeasure();
51786
51787         if(!c.dom.offsetWidth){ // display:none?
51788             if(initialRender){
51789                 this.lockedWrap.show();
51790                 this.mainWrap.show();
51791             }
51792             return;
51793         }
51794
51795         var hasLock = this.cm.isLocked(0);
51796
51797         var tbh = this.headerPanel.getHeight();
51798         var bbh = this.footerPanel.getHeight();
51799
51800         if(auto){
51801             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
51802             var newHeight = ch + c.getBorderWidth("tb");
51803             if(g.maxHeight){
51804                 newHeight = Math.min(g.maxHeight, newHeight);
51805             }
51806             c.setHeight(newHeight);
51807         }
51808
51809         if(g.autoWidth){
51810             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
51811         }
51812
51813         var s = this.scroller;
51814
51815         var csize = c.getSize(true);
51816
51817         this.el.setSize(csize.width, csize.height);
51818
51819         this.headerPanel.setWidth(csize.width);
51820         this.footerPanel.setWidth(csize.width);
51821
51822         var hdHeight = this.mainHd.getHeight();
51823         var vw = csize.width;
51824         var vh = csize.height - (tbh + bbh);
51825
51826         s.setSize(vw, vh);
51827
51828         var bt = this.getBodyTable();
51829         var ltWidth = hasLock ?
51830                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
51831
51832         var scrollHeight = bt.offsetHeight;
51833         var scrollWidth = ltWidth + bt.offsetWidth;
51834         var vscroll = false, hscroll = false;
51835
51836         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
51837
51838         var lw = this.lockedWrap, mw = this.mainWrap;
51839         var lb = this.lockedBody, mb = this.mainBody;
51840
51841         setTimeout(function(){
51842             var t = s.dom.offsetTop;
51843             var w = s.dom.clientWidth,
51844                 h = s.dom.clientHeight;
51845
51846             lw.setTop(t);
51847             lw.setSize(ltWidth, h);
51848
51849             mw.setLeftTop(ltWidth, t);
51850             mw.setSize(w-ltWidth, h);
51851
51852             lb.setHeight(h-hdHeight);
51853             mb.setHeight(h-hdHeight);
51854
51855             if(is2ndPass !== true && !gv.userResized && expandCol){
51856                 // high speed resize without full column calculation
51857                 
51858                 var ci = cm.getIndexById(expandCol);
51859                 if (ci < 0) {
51860                     ci = cm.findColumnIndex(expandCol);
51861                 }
51862                 ci = Math.max(0, ci); // make sure it's got at least the first col.
51863                 var expandId = cm.getColumnId(ci);
51864                 var  tw = cm.getTotalWidth(false);
51865                 var currentWidth = cm.getColumnWidth(ci);
51866                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
51867                 if(currentWidth != cw){
51868                     cm.setColumnWidth(ci, cw, true);
51869                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
51870                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
51871                     gv.updateSplitters();
51872                     gv.layout(false, true);
51873                 }
51874             }
51875
51876             if(initialRender){
51877                 lw.show();
51878                 mw.show();
51879             }
51880             //c.endMeasure();
51881         }, 10);
51882     },
51883
51884     onWindowResize : function(){
51885         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
51886             return;
51887         }
51888         this.layout();
51889     },
51890
51891     appendFooter : function(parentEl){
51892         return null;
51893     },
51894
51895     sortAscText : "Sort Ascending",
51896     sortDescText : "Sort Descending",
51897     lockText : "Lock Column",
51898     unlockText : "Unlock Column",
51899     columnsText : "Columns"
51900 });
51901
51902
51903 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
51904     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
51905     this.proxy.el.addClass('x-grid3-col-dd');
51906 };
51907
51908 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
51909     handleMouseDown : function(e){
51910
51911     },
51912
51913     callHandleMouseDown : function(e){
51914         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
51915     }
51916 });
51917 /*
51918  * Based on:
51919  * Ext JS Library 1.1.1
51920  * Copyright(c) 2006-2007, Ext JS, LLC.
51921  *
51922  * Originally Released Under LGPL - original licence link has changed is not relivant.
51923  *
51924  * Fork - LGPL
51925  * <script type="text/javascript">
51926  */
51927  
51928 // private
51929 // This is a support class used internally by the Grid components
51930 Roo.grid.SplitDragZone = function(grid, hd, hd2){
51931     this.grid = grid;
51932     this.view = grid.getView();
51933     this.proxy = this.view.resizeProxy;
51934     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
51935         "gridSplitters" + this.grid.getGridEl().id, {
51936         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
51937     });
51938     this.setHandleElId(Roo.id(hd));
51939     this.setOuterHandleElId(Roo.id(hd2));
51940     this.scroll = false;
51941 };
51942 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
51943     fly: Roo.Element.fly,
51944
51945     b4StartDrag : function(x, y){
51946         this.view.headersDisabled = true;
51947         this.proxy.setHeight(this.view.mainWrap.getHeight());
51948         var w = this.cm.getColumnWidth(this.cellIndex);
51949         var minw = Math.max(w-this.grid.minColumnWidth, 0);
51950         this.resetConstraints();
51951         this.setXConstraint(minw, 1000);
51952         this.setYConstraint(0, 0);
51953         this.minX = x - minw;
51954         this.maxX = x + 1000;
51955         this.startPos = x;
51956         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
51957     },
51958
51959
51960     handleMouseDown : function(e){
51961         ev = Roo.EventObject.setEvent(e);
51962         var t = this.fly(ev.getTarget());
51963         if(t.hasClass("x-grid-split")){
51964             this.cellIndex = this.view.getCellIndex(t.dom);
51965             this.split = t.dom;
51966             this.cm = this.grid.colModel;
51967             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
51968                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
51969             }
51970         }
51971     },
51972
51973     endDrag : function(e){
51974         this.view.headersDisabled = false;
51975         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
51976         var diff = endX - this.startPos;
51977         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
51978     },
51979
51980     autoOffset : function(){
51981         this.setDelta(0,0);
51982     }
51983 });/*
51984  * Based on:
51985  * Ext JS Library 1.1.1
51986  * Copyright(c) 2006-2007, Ext JS, LLC.
51987  *
51988  * Originally Released Under LGPL - original licence link has changed is not relivant.
51989  *
51990  * Fork - LGPL
51991  * <script type="text/javascript">
51992  */
51993  
51994 // private
51995 // This is a support class used internally by the Grid components
51996 Roo.grid.GridDragZone = function(grid, config){
51997     this.view = grid.getView();
51998     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
51999     if(this.view.lockedBody){
52000         this.setHandleElId(Roo.id(this.view.mainBody.dom));
52001         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
52002     }
52003     this.scroll = false;
52004     this.grid = grid;
52005     this.ddel = document.createElement('div');
52006     this.ddel.className = 'x-grid-dd-wrap';
52007 };
52008
52009 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
52010     ddGroup : "GridDD",
52011
52012     getDragData : function(e){
52013         var t = Roo.lib.Event.getTarget(e);
52014         var rowIndex = this.view.findRowIndex(t);
52015         var sm = this.grid.selModel;
52016             
52017         //Roo.log(rowIndex);
52018         
52019         if (sm.getSelectedCell) {
52020             // cell selection..
52021             if (!sm.getSelectedCell()) {
52022                 return false;
52023             }
52024             if (rowIndex != sm.getSelectedCell()[0]) {
52025                 return false;
52026             }
52027         
52028         }
52029         
52030         if(rowIndex !== false){
52031             
52032             // if editorgrid.. 
52033             
52034             
52035             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
52036                
52037             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
52038               //  
52039             //}
52040             if (e.hasModifier()){
52041                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
52042             }
52043             
52044             Roo.log("getDragData");
52045             
52046             return {
52047                 grid: this.grid,
52048                 ddel: this.ddel,
52049                 rowIndex: rowIndex,
52050                 selections:sm.getSelections ? sm.getSelections() : (
52051                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
52052                 )
52053             };
52054         }
52055         return false;
52056     },
52057
52058     onInitDrag : function(e){
52059         var data = this.dragData;
52060         this.ddel.innerHTML = this.grid.getDragDropText();
52061         this.proxy.update(this.ddel);
52062         // fire start drag?
52063     },
52064
52065     afterRepair : function(){
52066         this.dragging = false;
52067     },
52068
52069     getRepairXY : function(e, data){
52070         return false;
52071     },
52072
52073     onEndDrag : function(data, e){
52074         // fire end drag?
52075     },
52076
52077     onValidDrop : function(dd, e, id){
52078         // fire drag drop?
52079         this.hideProxy();
52080     },
52081
52082     beforeInvalidDrop : function(e, id){
52083
52084     }
52085 });/*
52086  * Based on:
52087  * Ext JS Library 1.1.1
52088  * Copyright(c) 2006-2007, Ext JS, LLC.
52089  *
52090  * Originally Released Under LGPL - original licence link has changed is not relivant.
52091  *
52092  * Fork - LGPL
52093  * <script type="text/javascript">
52094  */
52095  
52096
52097 /**
52098  * @class Roo.grid.ColumnModel
52099  * @extends Roo.util.Observable
52100  * This is the default implementation of a ColumnModel used by the Grid. It defines
52101  * the columns in the grid.
52102  * <br>Usage:<br>
52103  <pre><code>
52104  var colModel = new Roo.grid.ColumnModel([
52105         {header: "Ticker", width: 60, sortable: true, locked: true},
52106         {header: "Company Name", width: 150, sortable: true},
52107         {header: "Market Cap.", width: 100, sortable: true},
52108         {header: "$ Sales", width: 100, sortable: true, renderer: money},
52109         {header: "Employees", width: 100, sortable: true, resizable: false}
52110  ]);
52111  </code></pre>
52112  * <p>
52113  
52114  * The config options listed for this class are options which may appear in each
52115  * individual column definition.
52116  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
52117  * @constructor
52118  * @param {Object} config An Array of column config objects. See this class's
52119  * config objects for details.
52120 */
52121 Roo.grid.ColumnModel = function(config){
52122         /**
52123      * The config passed into the constructor
52124      */
52125     this.config = config;
52126     this.lookup = {};
52127
52128     // if no id, create one
52129     // if the column does not have a dataIndex mapping,
52130     // map it to the order it is in the config
52131     for(var i = 0, len = config.length; i < len; i++){
52132         var c = config[i];
52133         if(typeof c.dataIndex == "undefined"){
52134             c.dataIndex = i;
52135         }
52136         if(typeof c.renderer == "string"){
52137             c.renderer = Roo.util.Format[c.renderer];
52138         }
52139         if(typeof c.id == "undefined"){
52140             c.id = Roo.id();
52141         }
52142         if(c.editor && c.editor.xtype){
52143             c.editor  = Roo.factory(c.editor, Roo.grid);
52144         }
52145         if(c.editor && c.editor.isFormField){
52146             c.editor = new Roo.grid.GridEditor(c.editor);
52147         }
52148         this.lookup[c.id] = c;
52149     }
52150
52151     /**
52152      * The width of columns which have no width specified (defaults to 100)
52153      * @type Number
52154      */
52155     this.defaultWidth = 100;
52156
52157     /**
52158      * Default sortable of columns which have no sortable specified (defaults to false)
52159      * @type Boolean
52160      */
52161     this.defaultSortable = false;
52162
52163     this.addEvents({
52164         /**
52165              * @event widthchange
52166              * Fires when the width of a column changes.
52167              * @param {ColumnModel} this
52168              * @param {Number} columnIndex The column index
52169              * @param {Number} newWidth The new width
52170              */
52171             "widthchange": true,
52172         /**
52173              * @event headerchange
52174              * Fires when the text of a header changes.
52175              * @param {ColumnModel} this
52176              * @param {Number} columnIndex The column index
52177              * @param {Number} newText The new header text
52178              */
52179             "headerchange": true,
52180         /**
52181              * @event hiddenchange
52182              * Fires when a column is hidden or "unhidden".
52183              * @param {ColumnModel} this
52184              * @param {Number} columnIndex The column index
52185              * @param {Boolean} hidden true if hidden, false otherwise
52186              */
52187             "hiddenchange": true,
52188             /**
52189          * @event columnmoved
52190          * Fires when a column is moved.
52191          * @param {ColumnModel} this
52192          * @param {Number} oldIndex
52193          * @param {Number} newIndex
52194          */
52195         "columnmoved" : true,
52196         /**
52197          * @event columlockchange
52198          * Fires when a column's locked state is changed
52199          * @param {ColumnModel} this
52200          * @param {Number} colIndex
52201          * @param {Boolean} locked true if locked
52202          */
52203         "columnlockchange" : true
52204     });
52205     Roo.grid.ColumnModel.superclass.constructor.call(this);
52206 };
52207 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
52208     /**
52209      * @cfg {String} header The header text to display in the Grid view.
52210      */
52211     /**
52212      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
52213      * {@link Roo.data.Record} definition from which to draw the column's value. If not
52214      * specified, the column's index is used as an index into the Record's data Array.
52215      */
52216     /**
52217      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
52218      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
52219      */
52220     /**
52221      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
52222      * Defaults to the value of the {@link #defaultSortable} property.
52223      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
52224      */
52225     /**
52226      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
52227      */
52228     /**
52229      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
52230      */
52231     /**
52232      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
52233      */
52234     /**
52235      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
52236      */
52237     /**
52238      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
52239      * given the cell's data value. See {@link #setRenderer}. If not specified, the
52240      * default renderer uses the raw data value.
52241      */
52242        /**
52243      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
52244      */
52245     /**
52246      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
52247      */
52248
52249     /**
52250      * Returns the id of the column at the specified index.
52251      * @param {Number} index The column index
52252      * @return {String} the id
52253      */
52254     getColumnId : function(index){
52255         return this.config[index].id;
52256     },
52257
52258     /**
52259      * Returns the column for a specified id.
52260      * @param {String} id The column id
52261      * @return {Object} the column
52262      */
52263     getColumnById : function(id){
52264         return this.lookup[id];
52265     },
52266
52267     
52268     /**
52269      * Returns the column for a specified dataIndex.
52270      * @param {String} dataIndex The column dataIndex
52271      * @return {Object|Boolean} the column or false if not found
52272      */
52273     getColumnByDataIndex: function(dataIndex){
52274         var index = this.findColumnIndex(dataIndex);
52275         return index > -1 ? this.config[index] : false;
52276     },
52277     
52278     /**
52279      * Returns the index for a specified column id.
52280      * @param {String} id The column id
52281      * @return {Number} the index, or -1 if not found
52282      */
52283     getIndexById : function(id){
52284         for(var i = 0, len = this.config.length; i < len; i++){
52285             if(this.config[i].id == id){
52286                 return i;
52287             }
52288         }
52289         return -1;
52290     },
52291     
52292     /**
52293      * Returns the index for a specified column dataIndex.
52294      * @param {String} dataIndex The column dataIndex
52295      * @return {Number} the index, or -1 if not found
52296      */
52297     
52298     findColumnIndex : function(dataIndex){
52299         for(var i = 0, len = this.config.length; i < len; i++){
52300             if(this.config[i].dataIndex == dataIndex){
52301                 return i;
52302             }
52303         }
52304         return -1;
52305     },
52306     
52307     
52308     moveColumn : function(oldIndex, newIndex){
52309         var c = this.config[oldIndex];
52310         this.config.splice(oldIndex, 1);
52311         this.config.splice(newIndex, 0, c);
52312         this.dataMap = null;
52313         this.fireEvent("columnmoved", this, oldIndex, newIndex);
52314     },
52315
52316     isLocked : function(colIndex){
52317         return this.config[colIndex].locked === true;
52318     },
52319
52320     setLocked : function(colIndex, value, suppressEvent){
52321         if(this.isLocked(colIndex) == value){
52322             return;
52323         }
52324         this.config[colIndex].locked = value;
52325         if(!suppressEvent){
52326             this.fireEvent("columnlockchange", this, colIndex, value);
52327         }
52328     },
52329
52330     getTotalLockedWidth : function(){
52331         var totalWidth = 0;
52332         for(var i = 0; i < this.config.length; i++){
52333             if(this.isLocked(i) && !this.isHidden(i)){
52334                 this.totalWidth += this.getColumnWidth(i);
52335             }
52336         }
52337         return totalWidth;
52338     },
52339
52340     getLockedCount : function(){
52341         for(var i = 0, len = this.config.length; i < len; i++){
52342             if(!this.isLocked(i)){
52343                 return i;
52344             }
52345         }
52346     },
52347
52348     /**
52349      * Returns the number of columns.
52350      * @return {Number}
52351      */
52352     getColumnCount : function(visibleOnly){
52353         if(visibleOnly === true){
52354             var c = 0;
52355             for(var i = 0, len = this.config.length; i < len; i++){
52356                 if(!this.isHidden(i)){
52357                     c++;
52358                 }
52359             }
52360             return c;
52361         }
52362         return this.config.length;
52363     },
52364
52365     /**
52366      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
52367      * @param {Function} fn
52368      * @param {Object} scope (optional)
52369      * @return {Array} result
52370      */
52371     getColumnsBy : function(fn, scope){
52372         var r = [];
52373         for(var i = 0, len = this.config.length; i < len; i++){
52374             var c = this.config[i];
52375             if(fn.call(scope||this, c, i) === true){
52376                 r[r.length] = c;
52377             }
52378         }
52379         return r;
52380     },
52381
52382     /**
52383      * Returns true if the specified column is sortable.
52384      * @param {Number} col The column index
52385      * @return {Boolean}
52386      */
52387     isSortable : function(col){
52388         if(typeof this.config[col].sortable == "undefined"){
52389             return this.defaultSortable;
52390         }
52391         return this.config[col].sortable;
52392     },
52393
52394     /**
52395      * Returns the rendering (formatting) function defined for the column.
52396      * @param {Number} col The column index.
52397      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
52398      */
52399     getRenderer : function(col){
52400         if(!this.config[col].renderer){
52401             return Roo.grid.ColumnModel.defaultRenderer;
52402         }
52403         return this.config[col].renderer;
52404     },
52405
52406     /**
52407      * Sets the rendering (formatting) function for a column.
52408      * @param {Number} col The column index
52409      * @param {Function} fn The function to use to process the cell's raw data
52410      * to return HTML markup for the grid view. The render function is called with
52411      * the following parameters:<ul>
52412      * <li>Data value.</li>
52413      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
52414      * <li>css A CSS style string to apply to the table cell.</li>
52415      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
52416      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
52417      * <li>Row index</li>
52418      * <li>Column index</li>
52419      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
52420      */
52421     setRenderer : function(col, fn){
52422         this.config[col].renderer = fn;
52423     },
52424
52425     /**
52426      * Returns the width for the specified column.
52427      * @param {Number} col The column index
52428      * @return {Number}
52429      */
52430     getColumnWidth : function(col){
52431         return this.config[col].width * 1 || this.defaultWidth;
52432     },
52433
52434     /**
52435      * Sets the width for a column.
52436      * @param {Number} col The column index
52437      * @param {Number} width The new width
52438      */
52439     setColumnWidth : function(col, width, suppressEvent){
52440         this.config[col].width = width;
52441         this.totalWidth = null;
52442         if(!suppressEvent){
52443              this.fireEvent("widthchange", this, col, width);
52444         }
52445     },
52446
52447     /**
52448      * Returns the total width of all columns.
52449      * @param {Boolean} includeHidden True to include hidden column widths
52450      * @return {Number}
52451      */
52452     getTotalWidth : function(includeHidden){
52453         if(!this.totalWidth){
52454             this.totalWidth = 0;
52455             for(var i = 0, len = this.config.length; i < len; i++){
52456                 if(includeHidden || !this.isHidden(i)){
52457                     this.totalWidth += this.getColumnWidth(i);
52458                 }
52459             }
52460         }
52461         return this.totalWidth;
52462     },
52463
52464     /**
52465      * Returns the header for the specified column.
52466      * @param {Number} col The column index
52467      * @return {String}
52468      */
52469     getColumnHeader : function(col){
52470         return this.config[col].header;
52471     },
52472
52473     /**
52474      * Sets the header for a column.
52475      * @param {Number} col The column index
52476      * @param {String} header The new header
52477      */
52478     setColumnHeader : function(col, header){
52479         this.config[col].header = header;
52480         this.fireEvent("headerchange", this, col, header);
52481     },
52482
52483     /**
52484      * Returns the tooltip for the specified column.
52485      * @param {Number} col The column index
52486      * @return {String}
52487      */
52488     getColumnTooltip : function(col){
52489             return this.config[col].tooltip;
52490     },
52491     /**
52492      * Sets the tooltip for a column.
52493      * @param {Number} col The column index
52494      * @param {String} tooltip The new tooltip
52495      */
52496     setColumnTooltip : function(col, tooltip){
52497             this.config[col].tooltip = tooltip;
52498     },
52499
52500     /**
52501      * Returns the dataIndex for the specified column.
52502      * @param {Number} col The column index
52503      * @return {Number}
52504      */
52505     getDataIndex : function(col){
52506         return this.config[col].dataIndex;
52507     },
52508
52509     /**
52510      * Sets the dataIndex for a column.
52511      * @param {Number} col The column index
52512      * @param {Number} dataIndex The new dataIndex
52513      */
52514     setDataIndex : function(col, dataIndex){
52515         this.config[col].dataIndex = dataIndex;
52516     },
52517
52518     
52519     
52520     /**
52521      * Returns true if the cell is editable.
52522      * @param {Number} colIndex The column index
52523      * @param {Number} rowIndex The row index
52524      * @return {Boolean}
52525      */
52526     isCellEditable : function(colIndex, rowIndex){
52527         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
52528     },
52529
52530     /**
52531      * Returns the editor defined for the cell/column.
52532      * return false or null to disable editing.
52533      * @param {Number} colIndex The column index
52534      * @param {Number} rowIndex The row index
52535      * @return {Object}
52536      */
52537     getCellEditor : function(colIndex, rowIndex){
52538         return this.config[colIndex].editor;
52539     },
52540
52541     /**
52542      * Sets if a column is editable.
52543      * @param {Number} col The column index
52544      * @param {Boolean} editable True if the column is editable
52545      */
52546     setEditable : function(col, editable){
52547         this.config[col].editable = editable;
52548     },
52549
52550
52551     /**
52552      * Returns true if the column is hidden.
52553      * @param {Number} colIndex The column index
52554      * @return {Boolean}
52555      */
52556     isHidden : function(colIndex){
52557         return this.config[colIndex].hidden;
52558     },
52559
52560
52561     /**
52562      * Returns true if the column width cannot be changed
52563      */
52564     isFixed : function(colIndex){
52565         return this.config[colIndex].fixed;
52566     },
52567
52568     /**
52569      * Returns true if the column can be resized
52570      * @return {Boolean}
52571      */
52572     isResizable : function(colIndex){
52573         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
52574     },
52575     /**
52576      * Sets if a column is hidden.
52577      * @param {Number} colIndex The column index
52578      * @param {Boolean} hidden True if the column is hidden
52579      */
52580     setHidden : function(colIndex, hidden){
52581         this.config[colIndex].hidden = hidden;
52582         this.totalWidth = null;
52583         this.fireEvent("hiddenchange", this, colIndex, hidden);
52584     },
52585
52586     /**
52587      * Sets the editor for a column.
52588      * @param {Number} col The column index
52589      * @param {Object} editor The editor object
52590      */
52591     setEditor : function(col, editor){
52592         this.config[col].editor = editor;
52593     }
52594 });
52595
52596 Roo.grid.ColumnModel.defaultRenderer = function(value){
52597         if(typeof value == "string" && value.length < 1){
52598             return "&#160;";
52599         }
52600         return value;
52601 };
52602
52603 // Alias for backwards compatibility
52604 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
52605 /*
52606  * Based on:
52607  * Ext JS Library 1.1.1
52608  * Copyright(c) 2006-2007, Ext JS, LLC.
52609  *
52610  * Originally Released Under LGPL - original licence link has changed is not relivant.
52611  *
52612  * Fork - LGPL
52613  * <script type="text/javascript">
52614  */
52615
52616 /**
52617  * @class Roo.grid.AbstractSelectionModel
52618  * @extends Roo.util.Observable
52619  * Abstract base class for grid SelectionModels.  It provides the interface that should be
52620  * implemented by descendant classes.  This class should not be directly instantiated.
52621  * @constructor
52622  */
52623 Roo.grid.AbstractSelectionModel = function(){
52624     this.locked = false;
52625     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
52626 };
52627
52628 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
52629     /** @ignore Called by the grid automatically. Do not call directly. */
52630     init : function(grid){
52631         this.grid = grid;
52632         this.initEvents();
52633     },
52634
52635     /**
52636      * Locks the selections.
52637      */
52638     lock : function(){
52639         this.locked = true;
52640     },
52641
52642     /**
52643      * Unlocks the selections.
52644      */
52645     unlock : function(){
52646         this.locked = false;
52647     },
52648
52649     /**
52650      * Returns true if the selections are locked.
52651      * @return {Boolean}
52652      */
52653     isLocked : function(){
52654         return this.locked;
52655     }
52656 });/*
52657  * Based on:
52658  * Ext JS Library 1.1.1
52659  * Copyright(c) 2006-2007, Ext JS, LLC.
52660  *
52661  * Originally Released Under LGPL - original licence link has changed is not relivant.
52662  *
52663  * Fork - LGPL
52664  * <script type="text/javascript">
52665  */
52666 /**
52667  * @extends Roo.grid.AbstractSelectionModel
52668  * @class Roo.grid.RowSelectionModel
52669  * The default SelectionModel used by {@link Roo.grid.Grid}.
52670  * It supports multiple selections and keyboard selection/navigation. 
52671  * @constructor
52672  * @param {Object} config
52673  */
52674 Roo.grid.RowSelectionModel = function(config){
52675     Roo.apply(this, config);
52676     this.selections = new Roo.util.MixedCollection(false, function(o){
52677         return o.id;
52678     });
52679
52680     this.last = false;
52681     this.lastActive = false;
52682
52683     this.addEvents({
52684         /**
52685              * @event selectionchange
52686              * Fires when the selection changes
52687              * @param {SelectionModel} this
52688              */
52689             "selectionchange" : true,
52690         /**
52691              * @event afterselectionchange
52692              * Fires after the selection changes (eg. by key press or clicking)
52693              * @param {SelectionModel} this
52694              */
52695             "afterselectionchange" : true,
52696         /**
52697              * @event beforerowselect
52698              * Fires when a row is selected being selected, return false to cancel.
52699              * @param {SelectionModel} this
52700              * @param {Number} rowIndex The selected index
52701              * @param {Boolean} keepExisting False if other selections will be cleared
52702              */
52703             "beforerowselect" : true,
52704         /**
52705              * @event rowselect
52706              * Fires when a row is selected.
52707              * @param {SelectionModel} this
52708              * @param {Number} rowIndex The selected index
52709              * @param {Roo.data.Record} r The record
52710              */
52711             "rowselect" : true,
52712         /**
52713              * @event rowdeselect
52714              * Fires when a row is deselected.
52715              * @param {SelectionModel} this
52716              * @param {Number} rowIndex The selected index
52717              */
52718         "rowdeselect" : true
52719     });
52720     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
52721     this.locked = false;
52722 };
52723
52724 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
52725     /**
52726      * @cfg {Boolean} singleSelect
52727      * True to allow selection of only one row at a time (defaults to false)
52728      */
52729     singleSelect : false,
52730
52731     // private
52732     initEvents : function(){
52733
52734         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
52735             this.grid.on("mousedown", this.handleMouseDown, this);
52736         }else{ // allow click to work like normal
52737             this.grid.on("rowclick", this.handleDragableRowClick, this);
52738         }
52739
52740         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
52741             "up" : function(e){
52742                 if(!e.shiftKey){
52743                     this.selectPrevious(e.shiftKey);
52744                 }else if(this.last !== false && this.lastActive !== false){
52745                     var last = this.last;
52746                     this.selectRange(this.last,  this.lastActive-1);
52747                     this.grid.getView().focusRow(this.lastActive);
52748                     if(last !== false){
52749                         this.last = last;
52750                     }
52751                 }else{
52752                     this.selectFirstRow();
52753                 }
52754                 this.fireEvent("afterselectionchange", this);
52755             },
52756             "down" : function(e){
52757                 if(!e.shiftKey){
52758                     this.selectNext(e.shiftKey);
52759                 }else if(this.last !== false && this.lastActive !== false){
52760                     var last = this.last;
52761                     this.selectRange(this.last,  this.lastActive+1);
52762                     this.grid.getView().focusRow(this.lastActive);
52763                     if(last !== false){
52764                         this.last = last;
52765                     }
52766                 }else{
52767                     this.selectFirstRow();
52768                 }
52769                 this.fireEvent("afterselectionchange", this);
52770             },
52771             scope: this
52772         });
52773
52774         var view = this.grid.view;
52775         view.on("refresh", this.onRefresh, this);
52776         view.on("rowupdated", this.onRowUpdated, this);
52777         view.on("rowremoved", this.onRemove, this);
52778     },
52779
52780     // private
52781     onRefresh : function(){
52782         var ds = this.grid.dataSource, i, v = this.grid.view;
52783         var s = this.selections;
52784         s.each(function(r){
52785             if((i = ds.indexOfId(r.id)) != -1){
52786                 v.onRowSelect(i);
52787             }else{
52788                 s.remove(r);
52789             }
52790         });
52791     },
52792
52793     // private
52794     onRemove : function(v, index, r){
52795         this.selections.remove(r);
52796     },
52797
52798     // private
52799     onRowUpdated : function(v, index, r){
52800         if(this.isSelected(r)){
52801             v.onRowSelect(index);
52802         }
52803     },
52804
52805     /**
52806      * Select records.
52807      * @param {Array} records The records to select
52808      * @param {Boolean} keepExisting (optional) True to keep existing selections
52809      */
52810     selectRecords : function(records, keepExisting){
52811         if(!keepExisting){
52812             this.clearSelections();
52813         }
52814         var ds = this.grid.dataSource;
52815         for(var i = 0, len = records.length; i < len; i++){
52816             this.selectRow(ds.indexOf(records[i]), true);
52817         }
52818     },
52819
52820     /**
52821      * Gets the number of selected rows.
52822      * @return {Number}
52823      */
52824     getCount : function(){
52825         return this.selections.length;
52826     },
52827
52828     /**
52829      * Selects the first row in the grid.
52830      */
52831     selectFirstRow : function(){
52832         this.selectRow(0);
52833     },
52834
52835     /**
52836      * Select the last row.
52837      * @param {Boolean} keepExisting (optional) True to keep existing selections
52838      */
52839     selectLastRow : function(keepExisting){
52840         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
52841     },
52842
52843     /**
52844      * Selects the row immediately following the last selected row.
52845      * @param {Boolean} keepExisting (optional) True to keep existing selections
52846      */
52847     selectNext : function(keepExisting){
52848         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
52849             this.selectRow(this.last+1, keepExisting);
52850             this.grid.getView().focusRow(this.last);
52851         }
52852     },
52853
52854     /**
52855      * Selects the row that precedes the last selected row.
52856      * @param {Boolean} keepExisting (optional) True to keep existing selections
52857      */
52858     selectPrevious : function(keepExisting){
52859         if(this.last){
52860             this.selectRow(this.last-1, keepExisting);
52861             this.grid.getView().focusRow(this.last);
52862         }
52863     },
52864
52865     /**
52866      * Returns the selected records
52867      * @return {Array} Array of selected records
52868      */
52869     getSelections : function(){
52870         return [].concat(this.selections.items);
52871     },
52872
52873     /**
52874      * Returns the first selected record.
52875      * @return {Record}
52876      */
52877     getSelected : function(){
52878         return this.selections.itemAt(0);
52879     },
52880
52881
52882     /**
52883      * Clears all selections.
52884      */
52885     clearSelections : function(fast){
52886         if(this.locked) return;
52887         if(fast !== true){
52888             var ds = this.grid.dataSource;
52889             var s = this.selections;
52890             s.each(function(r){
52891                 this.deselectRow(ds.indexOfId(r.id));
52892             }, this);
52893             s.clear();
52894         }else{
52895             this.selections.clear();
52896         }
52897         this.last = false;
52898     },
52899
52900
52901     /**
52902      * Selects all rows.
52903      */
52904     selectAll : function(){
52905         if(this.locked) return;
52906         this.selections.clear();
52907         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
52908             this.selectRow(i, true);
52909         }
52910     },
52911
52912     /**
52913      * Returns True if there is a selection.
52914      * @return {Boolean}
52915      */
52916     hasSelection : function(){
52917         return this.selections.length > 0;
52918     },
52919
52920     /**
52921      * Returns True if the specified row is selected.
52922      * @param {Number/Record} record The record or index of the record to check
52923      * @return {Boolean}
52924      */
52925     isSelected : function(index){
52926         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
52927         return (r && this.selections.key(r.id) ? true : false);
52928     },
52929
52930     /**
52931      * Returns True if the specified record id is selected.
52932      * @param {String} id The id of record to check
52933      * @return {Boolean}
52934      */
52935     isIdSelected : function(id){
52936         return (this.selections.key(id) ? true : false);
52937     },
52938
52939     // private
52940     handleMouseDown : function(e, t){
52941         var view = this.grid.getView(), rowIndex;
52942         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
52943             return;
52944         };
52945         if(e.shiftKey && this.last !== false){
52946             var last = this.last;
52947             this.selectRange(last, rowIndex, e.ctrlKey);
52948             this.last = last; // reset the last
52949             view.focusRow(rowIndex);
52950         }else{
52951             var isSelected = this.isSelected(rowIndex);
52952             if(e.button !== 0 && isSelected){
52953                 view.focusRow(rowIndex);
52954             }else if(e.ctrlKey && isSelected){
52955                 this.deselectRow(rowIndex);
52956             }else if(!isSelected){
52957                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
52958                 view.focusRow(rowIndex);
52959             }
52960         }
52961         this.fireEvent("afterselectionchange", this);
52962     },
52963     // private
52964     handleDragableRowClick :  function(grid, rowIndex, e) 
52965     {
52966         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
52967             this.selectRow(rowIndex, false);
52968             grid.view.focusRow(rowIndex);
52969              this.fireEvent("afterselectionchange", this);
52970         }
52971     },
52972     
52973     /**
52974      * Selects multiple rows.
52975      * @param {Array} rows Array of the indexes of the row to select
52976      * @param {Boolean} keepExisting (optional) True to keep existing selections
52977      */
52978     selectRows : function(rows, keepExisting){
52979         if(!keepExisting){
52980             this.clearSelections();
52981         }
52982         for(var i = 0, len = rows.length; i < len; i++){
52983             this.selectRow(rows[i], true);
52984         }
52985     },
52986
52987     /**
52988      * Selects a range of rows. All rows in between startRow and endRow are also selected.
52989      * @param {Number} startRow The index of the first row in the range
52990      * @param {Number} endRow The index of the last row in the range
52991      * @param {Boolean} keepExisting (optional) True to retain existing selections
52992      */
52993     selectRange : function(startRow, endRow, keepExisting){
52994         if(this.locked) return;
52995         if(!keepExisting){
52996             this.clearSelections();
52997         }
52998         if(startRow <= endRow){
52999             for(var i = startRow; i <= endRow; i++){
53000                 this.selectRow(i, true);
53001             }
53002         }else{
53003             for(var i = startRow; i >= endRow; i--){
53004                 this.selectRow(i, true);
53005             }
53006         }
53007     },
53008
53009     /**
53010      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
53011      * @param {Number} startRow The index of the first row in the range
53012      * @param {Number} endRow The index of the last row in the range
53013      */
53014     deselectRange : function(startRow, endRow, preventViewNotify){
53015         if(this.locked) return;
53016         for(var i = startRow; i <= endRow; i++){
53017             this.deselectRow(i, preventViewNotify);
53018         }
53019     },
53020
53021     /**
53022      * Selects a row.
53023      * @param {Number} row The index of the row to select
53024      * @param {Boolean} keepExisting (optional) True to keep existing selections
53025      */
53026     selectRow : function(index, keepExisting, preventViewNotify){
53027         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
53028         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
53029             if(!keepExisting || this.singleSelect){
53030                 this.clearSelections();
53031             }
53032             var r = this.grid.dataSource.getAt(index);
53033             this.selections.add(r);
53034             this.last = this.lastActive = index;
53035             if(!preventViewNotify){
53036                 this.grid.getView().onRowSelect(index);
53037             }
53038             this.fireEvent("rowselect", this, index, r);
53039             this.fireEvent("selectionchange", this);
53040         }
53041     },
53042
53043     /**
53044      * Deselects a row.
53045      * @param {Number} row The index of the row to deselect
53046      */
53047     deselectRow : function(index, preventViewNotify){
53048         if(this.locked) return;
53049         if(this.last == index){
53050             this.last = false;
53051         }
53052         if(this.lastActive == index){
53053             this.lastActive = false;
53054         }
53055         var r = this.grid.dataSource.getAt(index);
53056         this.selections.remove(r);
53057         if(!preventViewNotify){
53058             this.grid.getView().onRowDeselect(index);
53059         }
53060         this.fireEvent("rowdeselect", this, index);
53061         this.fireEvent("selectionchange", this);
53062     },
53063
53064     // private
53065     restoreLast : function(){
53066         if(this._last){
53067             this.last = this._last;
53068         }
53069     },
53070
53071     // private
53072     acceptsNav : function(row, col, cm){
53073         return !cm.isHidden(col) && cm.isCellEditable(col, row);
53074     },
53075
53076     // private
53077     onEditorKey : function(field, e){
53078         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
53079         if(k == e.TAB){
53080             e.stopEvent();
53081             ed.completeEdit();
53082             if(e.shiftKey){
53083                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
53084             }else{
53085                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53086             }
53087         }else if(k == e.ENTER && !e.ctrlKey){
53088             e.stopEvent();
53089             ed.completeEdit();
53090             if(e.shiftKey){
53091                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
53092             }else{
53093                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
53094             }
53095         }else if(k == e.ESC){
53096             ed.cancelEdit();
53097         }
53098         if(newCell){
53099             g.startEditing(newCell[0], newCell[1]);
53100         }
53101     }
53102 });/*
53103  * Based on:
53104  * Ext JS Library 1.1.1
53105  * Copyright(c) 2006-2007, Ext JS, LLC.
53106  *
53107  * Originally Released Under LGPL - original licence link has changed is not relivant.
53108  *
53109  * Fork - LGPL
53110  * <script type="text/javascript">
53111  */
53112 /**
53113  * @class Roo.grid.CellSelectionModel
53114  * @extends Roo.grid.AbstractSelectionModel
53115  * This class provides the basic implementation for cell selection in a grid.
53116  * @constructor
53117  * @param {Object} config The object containing the configuration of this model.
53118  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
53119  */
53120 Roo.grid.CellSelectionModel = function(config){
53121     Roo.apply(this, config);
53122
53123     this.selection = null;
53124
53125     this.addEvents({
53126         /**
53127              * @event beforerowselect
53128              * Fires before a cell is selected.
53129              * @param {SelectionModel} this
53130              * @param {Number} rowIndex The selected row index
53131              * @param {Number} colIndex The selected cell index
53132              */
53133             "beforecellselect" : true,
53134         /**
53135              * @event cellselect
53136              * Fires when a cell is selected.
53137              * @param {SelectionModel} this
53138              * @param {Number} rowIndex The selected row index
53139              * @param {Number} colIndex The selected cell index
53140              */
53141             "cellselect" : true,
53142         /**
53143              * @event selectionchange
53144              * Fires when the active selection changes.
53145              * @param {SelectionModel} this
53146              * @param {Object} selection null for no selection or an object (o) with two properties
53147                 <ul>
53148                 <li>o.record: the record object for the row the selection is in</li>
53149                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
53150                 </ul>
53151              */
53152             "selectionchange" : true,
53153         /**
53154              * @event tabend
53155              * Fires when the tab (or enter) was pressed on the last editable cell
53156              * You can use this to trigger add new row.
53157              * @param {SelectionModel} this
53158              */
53159             "tabend" : true,
53160          /**
53161              * @event beforeeditnext
53162              * Fires before the next editable sell is made active
53163              * You can use this to skip to another cell or fire the tabend
53164              *    if you set cell to false
53165              * @param {Object} eventdata object : { cell : [ row, col ] } 
53166              */
53167             "beforeeditnext" : true
53168     });
53169     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
53170 };
53171
53172 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
53173     
53174     enter_is_tab: false,
53175
53176     /** @ignore */
53177     initEvents : function(){
53178         this.grid.on("mousedown", this.handleMouseDown, this);
53179         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
53180         var view = this.grid.view;
53181         view.on("refresh", this.onViewChange, this);
53182         view.on("rowupdated", this.onRowUpdated, this);
53183         view.on("beforerowremoved", this.clearSelections, this);
53184         view.on("beforerowsinserted", this.clearSelections, this);
53185         if(this.grid.isEditor){
53186             this.grid.on("beforeedit", this.beforeEdit,  this);
53187         }
53188     },
53189
53190         //private
53191     beforeEdit : function(e){
53192         this.select(e.row, e.column, false, true, e.record);
53193     },
53194
53195         //private
53196     onRowUpdated : function(v, index, r){
53197         if(this.selection && this.selection.record == r){
53198             v.onCellSelect(index, this.selection.cell[1]);
53199         }
53200     },
53201
53202         //private
53203     onViewChange : function(){
53204         this.clearSelections(true);
53205     },
53206
53207         /**
53208          * Returns the currently selected cell,.
53209          * @return {Array} The selected cell (row, column) or null if none selected.
53210          */
53211     getSelectedCell : function(){
53212         return this.selection ? this.selection.cell : null;
53213     },
53214
53215     /**
53216      * Clears all selections.
53217      * @param {Boolean} true to prevent the gridview from being notified about the change.
53218      */
53219     clearSelections : function(preventNotify){
53220         var s = this.selection;
53221         if(s){
53222             if(preventNotify !== true){
53223                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
53224             }
53225             this.selection = null;
53226             this.fireEvent("selectionchange", this, null);
53227         }
53228     },
53229
53230     /**
53231      * Returns true if there is a selection.
53232      * @return {Boolean}
53233      */
53234     hasSelection : function(){
53235         return this.selection ? true : false;
53236     },
53237
53238     /** @ignore */
53239     handleMouseDown : function(e, t){
53240         var v = this.grid.getView();
53241         if(this.isLocked()){
53242             return;
53243         };
53244         var row = v.findRowIndex(t);
53245         var cell = v.findCellIndex(t);
53246         if(row !== false && cell !== false){
53247             this.select(row, cell);
53248         }
53249     },
53250
53251     /**
53252      * Selects a cell.
53253      * @param {Number} rowIndex
53254      * @param {Number} collIndex
53255      */
53256     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
53257         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
53258             this.clearSelections();
53259             r = r || this.grid.dataSource.getAt(rowIndex);
53260             this.selection = {
53261                 record : r,
53262                 cell : [rowIndex, colIndex]
53263             };
53264             if(!preventViewNotify){
53265                 var v = this.grid.getView();
53266                 v.onCellSelect(rowIndex, colIndex);
53267                 if(preventFocus !== true){
53268                     v.focusCell(rowIndex, colIndex);
53269                 }
53270             }
53271             this.fireEvent("cellselect", this, rowIndex, colIndex);
53272             this.fireEvent("selectionchange", this, this.selection);
53273         }
53274     },
53275
53276         //private
53277     isSelectable : function(rowIndex, colIndex, cm){
53278         return !cm.isHidden(colIndex);
53279     },
53280
53281     /** @ignore */
53282     handleKeyDown : function(e){
53283         //Roo.log('Cell Sel Model handleKeyDown');
53284         if(!e.isNavKeyPress()){
53285             return;
53286         }
53287         var g = this.grid, s = this.selection;
53288         if(!s){
53289             e.stopEvent();
53290             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
53291             if(cell){
53292                 this.select(cell[0], cell[1]);
53293             }
53294             return;
53295         }
53296         var sm = this;
53297         var walk = function(row, col, step){
53298             return g.walkCells(row, col, step, sm.isSelectable,  sm);
53299         };
53300         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
53301         var newCell;
53302
53303       
53304
53305         switch(k){
53306             case e.TAB:
53307                 // handled by onEditorKey
53308                 if (g.isEditor && g.editing) {
53309                     return;
53310                 }
53311                 if(e.shiftKey) {
53312                     newCell = walk(r, c-1, -1);
53313                 } else {
53314                     newCell = walk(r, c+1, 1);
53315                 }
53316                 break;
53317             
53318             case e.DOWN:
53319                newCell = walk(r+1, c, 1);
53320                 break;
53321             
53322             case e.UP:
53323                 newCell = walk(r-1, c, -1);
53324                 break;
53325             
53326             case e.RIGHT:
53327                 newCell = walk(r, c+1, 1);
53328                 break;
53329             
53330             case e.LEFT:
53331                 newCell = walk(r, c-1, -1);
53332                 break;
53333             
53334             case e.ENTER:
53335                 
53336                 if(g.isEditor && !g.editing){
53337                    g.startEditing(r, c);
53338                    e.stopEvent();
53339                    return;
53340                 }
53341                 
53342                 
53343              break;
53344         };
53345         if(newCell){
53346             this.select(newCell[0], newCell[1]);
53347             e.stopEvent();
53348             
53349         }
53350     },
53351
53352     acceptsNav : function(row, col, cm){
53353         return !cm.isHidden(col) && cm.isCellEditable(col, row);
53354     },
53355     /**
53356      * Selects a cell.
53357      * @param {Number} field (not used) - as it's normally used as a listener
53358      * @param {Number} e - event - fake it by using
53359      *
53360      * var e = Roo.EventObjectImpl.prototype;
53361      * e.keyCode = e.TAB
53362      *
53363      * 
53364      */
53365     onEditorKey : function(field, e){
53366         
53367         var k = e.getKey(),
53368             newCell,
53369             g = this.grid,
53370             ed = g.activeEditor,
53371             forward = false;
53372         ///Roo.log('onEditorKey' + k);
53373         
53374         
53375         if (this.enter_is_tab && k == e.ENTER) {
53376             k = e.TAB;
53377         }
53378         
53379         if(k == e.TAB){
53380             if(e.shiftKey){
53381                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
53382             }else{
53383                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53384                 forward = true;
53385             }
53386             
53387             e.stopEvent();
53388             
53389         } else if(k == e.ENTER &&  !e.ctrlKey){
53390             ed.completeEdit();
53391             e.stopEvent();
53392             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53393         
53394                 } else if(k == e.ESC){
53395             ed.cancelEdit();
53396         }
53397                 
53398         if (newCell) {
53399             var ecall = { cell : newCell, forward : forward };
53400             this.fireEvent('beforeeditnext', ecall );
53401             newCell = ecall.cell;
53402                         forward = ecall.forward;
53403         }
53404                 
53405         if(newCell){
53406             //Roo.log('next cell after edit');
53407             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
53408         } else if (forward) {
53409             // tabbed past last
53410             this.fireEvent.defer(100, this, ['tabend',this]);
53411         }
53412     }
53413 });/*
53414  * Based on:
53415  * Ext JS Library 1.1.1
53416  * Copyright(c) 2006-2007, Ext JS, LLC.
53417  *
53418  * Originally Released Under LGPL - original licence link has changed is not relivant.
53419  *
53420  * Fork - LGPL
53421  * <script type="text/javascript">
53422  */
53423  
53424 /**
53425  * @class Roo.grid.EditorGrid
53426  * @extends Roo.grid.Grid
53427  * Class for creating and editable grid.
53428  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
53429  * The container MUST have some type of size defined for the grid to fill. The container will be 
53430  * automatically set to position relative if it isn't already.
53431  * @param {Object} dataSource The data model to bind to
53432  * @param {Object} colModel The column model with info about this grid's columns
53433  */
53434 Roo.grid.EditorGrid = function(container, config){
53435     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
53436     this.getGridEl().addClass("xedit-grid");
53437
53438     if(!this.selModel){
53439         this.selModel = new Roo.grid.CellSelectionModel();
53440     }
53441
53442     this.activeEditor = null;
53443
53444         this.addEvents({
53445             /**
53446              * @event beforeedit
53447              * Fires before cell editing is triggered. The edit event object has the following properties <br />
53448              * <ul style="padding:5px;padding-left:16px;">
53449              * <li>grid - This grid</li>
53450              * <li>record - The record being edited</li>
53451              * <li>field - The field name being edited</li>
53452              * <li>value - The value for the field being edited.</li>
53453              * <li>row - The grid row index</li>
53454              * <li>column - The grid column index</li>
53455              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
53456              * </ul>
53457              * @param {Object} e An edit event (see above for description)
53458              */
53459             "beforeedit" : true,
53460             /**
53461              * @event afteredit
53462              * Fires after a cell is edited. <br />
53463              * <ul style="padding:5px;padding-left:16px;">
53464              * <li>grid - This grid</li>
53465              * <li>record - The record being edited</li>
53466              * <li>field - The field name being edited</li>
53467              * <li>value - The value being set</li>
53468              * <li>originalValue - The original value for the field, before the edit.</li>
53469              * <li>row - The grid row index</li>
53470              * <li>column - The grid column index</li>
53471              * </ul>
53472              * @param {Object} e An edit event (see above for description)
53473              */
53474             "afteredit" : true,
53475             /**
53476              * @event validateedit
53477              * Fires after a cell is edited, but before the value is set in the record. 
53478          * You can use this to modify the value being set in the field, Return false
53479              * to cancel the change. The edit event object has the following properties <br />
53480              * <ul style="padding:5px;padding-left:16px;">
53481          * <li>editor - This editor</li>
53482              * <li>grid - This grid</li>
53483              * <li>record - The record being edited</li>
53484              * <li>field - The field name being edited</li>
53485              * <li>value - The value being set</li>
53486              * <li>originalValue - The original value for the field, before the edit.</li>
53487              * <li>row - The grid row index</li>
53488              * <li>column - The grid column index</li>
53489              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
53490              * </ul>
53491              * @param {Object} e An edit event (see above for description)
53492              */
53493             "validateedit" : true
53494         });
53495     this.on("bodyscroll", this.stopEditing,  this);
53496     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
53497 };
53498
53499 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
53500     /**
53501      * @cfg {Number} clicksToEdit
53502      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
53503      */
53504     clicksToEdit: 2,
53505
53506     // private
53507     isEditor : true,
53508     // private
53509     trackMouseOver: false, // causes very odd FF errors
53510
53511     onCellDblClick : function(g, row, col){
53512         this.startEditing(row, col);
53513     },
53514
53515     onEditComplete : function(ed, value, startValue){
53516         this.editing = false;
53517         this.activeEditor = null;
53518         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
53519         var r = ed.record;
53520         var field = this.colModel.getDataIndex(ed.col);
53521         var e = {
53522             grid: this,
53523             record: r,
53524             field: field,
53525             originalValue: startValue,
53526             value: value,
53527             row: ed.row,
53528             column: ed.col,
53529             cancel:false,
53530             editor: ed
53531         };
53532         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
53533         cell.show();
53534           
53535         if(String(value) !== String(startValue)){
53536             
53537             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
53538                 r.set(field, e.value);
53539                 // if we are dealing with a combo box..
53540                 // then we also set the 'name' colum to be the displayField
53541                 if (ed.field.displayField && ed.field.name) {
53542                     r.set(ed.field.name, ed.field.el.dom.value);
53543                 }
53544                 
53545                 delete e.cancel; //?? why!!!
53546                 this.fireEvent("afteredit", e);
53547             }
53548         } else {
53549             this.fireEvent("afteredit", e); // always fire it!
53550         }
53551         this.view.focusCell(ed.row, ed.col);
53552     },
53553
53554     /**
53555      * Starts editing the specified for the specified row/column
53556      * @param {Number} rowIndex
53557      * @param {Number} colIndex
53558      */
53559     startEditing : function(row, col){
53560         this.stopEditing();
53561         if(this.colModel.isCellEditable(col, row)){
53562             this.view.ensureVisible(row, col, true);
53563           
53564             var r = this.dataSource.getAt(row);
53565             var field = this.colModel.getDataIndex(col);
53566             var cell = Roo.get(this.view.getCell(row,col));
53567             var e = {
53568                 grid: this,
53569                 record: r,
53570                 field: field,
53571                 value: r.data[field],
53572                 row: row,
53573                 column: col,
53574                 cancel:false 
53575             };
53576             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
53577                 this.editing = true;
53578                 var ed = this.colModel.getCellEditor(col, row);
53579                 
53580                 if (!ed) {
53581                     return;
53582                 }
53583                 if(!ed.rendered){
53584                     ed.render(ed.parentEl || document.body);
53585                 }
53586                 ed.field.reset();
53587                
53588                 cell.hide();
53589                 
53590                 (function(){ // complex but required for focus issues in safari, ie and opera
53591                     ed.row = row;
53592                     ed.col = col;
53593                     ed.record = r;
53594                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
53595                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
53596                     this.activeEditor = ed;
53597                     var v = r.data[field];
53598                     ed.startEdit(this.view.getCell(row, col), v);
53599                     // combo's with 'displayField and name set
53600                     if (ed.field.displayField && ed.field.name) {
53601                         ed.field.el.dom.value = r.data[ed.field.name];
53602                     }
53603                     
53604                     
53605                 }).defer(50, this);
53606             }
53607         }
53608     },
53609         
53610     /**
53611      * Stops any active editing
53612      */
53613     stopEditing : function(){
53614         if(this.activeEditor){
53615             this.activeEditor.completeEdit();
53616         }
53617         this.activeEditor = null;
53618     },
53619         
53620          /**
53621      * Called to get grid's drag proxy text, by default returns this.ddText.
53622      * @return {String}
53623      */
53624     getDragDropText : function(){
53625         var count = this.selModel.getSelectedCell() ? 1 : 0;
53626         return String.format(this.ddText, count, count == 1 ? '' : 's');
53627     }
53628         
53629 });/*
53630  * Based on:
53631  * Ext JS Library 1.1.1
53632  * Copyright(c) 2006-2007, Ext JS, LLC.
53633  *
53634  * Originally Released Under LGPL - original licence link has changed is not relivant.
53635  *
53636  * Fork - LGPL
53637  * <script type="text/javascript">
53638  */
53639
53640 // private - not really -- you end up using it !
53641 // This is a support class used internally by the Grid components
53642
53643 /**
53644  * @class Roo.grid.GridEditor
53645  * @extends Roo.Editor
53646  * Class for creating and editable grid elements.
53647  * @param {Object} config any settings (must include field)
53648  */
53649 Roo.grid.GridEditor = function(field, config){
53650     if (!config && field.field) {
53651         config = field;
53652         field = Roo.factory(config.field, Roo.form);
53653     }
53654     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
53655     field.monitorTab = false;
53656 };
53657
53658 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
53659     
53660     /**
53661      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
53662      */
53663     
53664     alignment: "tl-tl",
53665     autoSize: "width",
53666     hideEl : false,
53667     cls: "x-small-editor x-grid-editor",
53668     shim:false,
53669     shadow:"frame"
53670 });/*
53671  * Based on:
53672  * Ext JS Library 1.1.1
53673  * Copyright(c) 2006-2007, Ext JS, LLC.
53674  *
53675  * Originally Released Under LGPL - original licence link has changed is not relivant.
53676  *
53677  * Fork - LGPL
53678  * <script type="text/javascript">
53679  */
53680   
53681
53682   
53683 Roo.grid.PropertyRecord = Roo.data.Record.create([
53684     {name:'name',type:'string'},  'value'
53685 ]);
53686
53687
53688 Roo.grid.PropertyStore = function(grid, source){
53689     this.grid = grid;
53690     this.store = new Roo.data.Store({
53691         recordType : Roo.grid.PropertyRecord
53692     });
53693     this.store.on('update', this.onUpdate,  this);
53694     if(source){
53695         this.setSource(source);
53696     }
53697     Roo.grid.PropertyStore.superclass.constructor.call(this);
53698 };
53699
53700
53701
53702 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
53703     setSource : function(o){
53704         this.source = o;
53705         this.store.removeAll();
53706         var data = [];
53707         for(var k in o){
53708             if(this.isEditableValue(o[k])){
53709                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
53710             }
53711         }
53712         this.store.loadRecords({records: data}, {}, true);
53713     },
53714
53715     onUpdate : function(ds, record, type){
53716         if(type == Roo.data.Record.EDIT){
53717             var v = record.data['value'];
53718             var oldValue = record.modified['value'];
53719             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
53720                 this.source[record.id] = v;
53721                 record.commit();
53722                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
53723             }else{
53724                 record.reject();
53725             }
53726         }
53727     },
53728
53729     getProperty : function(row){
53730        return this.store.getAt(row);
53731     },
53732
53733     isEditableValue: function(val){
53734         if(val && val instanceof Date){
53735             return true;
53736         }else if(typeof val == 'object' || typeof val == 'function'){
53737             return false;
53738         }
53739         return true;
53740     },
53741
53742     setValue : function(prop, value){
53743         this.source[prop] = value;
53744         this.store.getById(prop).set('value', value);
53745     },
53746
53747     getSource : function(){
53748         return this.source;
53749     }
53750 });
53751
53752 Roo.grid.PropertyColumnModel = function(grid, store){
53753     this.grid = grid;
53754     var g = Roo.grid;
53755     g.PropertyColumnModel.superclass.constructor.call(this, [
53756         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
53757         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
53758     ]);
53759     this.store = store;
53760     this.bselect = Roo.DomHelper.append(document.body, {
53761         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
53762             {tag: 'option', value: 'true', html: 'true'},
53763             {tag: 'option', value: 'false', html: 'false'}
53764         ]
53765     });
53766     Roo.id(this.bselect);
53767     var f = Roo.form;
53768     this.editors = {
53769         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
53770         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
53771         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
53772         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
53773         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
53774     };
53775     this.renderCellDelegate = this.renderCell.createDelegate(this);
53776     this.renderPropDelegate = this.renderProp.createDelegate(this);
53777 };
53778
53779 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
53780     
53781     
53782     nameText : 'Name',
53783     valueText : 'Value',
53784     
53785     dateFormat : 'm/j/Y',
53786     
53787     
53788     renderDate : function(dateVal){
53789         return dateVal.dateFormat(this.dateFormat);
53790     },
53791
53792     renderBool : function(bVal){
53793         return bVal ? 'true' : 'false';
53794     },
53795
53796     isCellEditable : function(colIndex, rowIndex){
53797         return colIndex == 1;
53798     },
53799
53800     getRenderer : function(col){
53801         return col == 1 ?
53802             this.renderCellDelegate : this.renderPropDelegate;
53803     },
53804
53805     renderProp : function(v){
53806         return this.getPropertyName(v);
53807     },
53808
53809     renderCell : function(val){
53810         var rv = val;
53811         if(val instanceof Date){
53812             rv = this.renderDate(val);
53813         }else if(typeof val == 'boolean'){
53814             rv = this.renderBool(val);
53815         }
53816         return Roo.util.Format.htmlEncode(rv);
53817     },
53818
53819     getPropertyName : function(name){
53820         var pn = this.grid.propertyNames;
53821         return pn && pn[name] ? pn[name] : name;
53822     },
53823
53824     getCellEditor : function(colIndex, rowIndex){
53825         var p = this.store.getProperty(rowIndex);
53826         var n = p.data['name'], val = p.data['value'];
53827         
53828         if(typeof(this.grid.customEditors[n]) == 'string'){
53829             return this.editors[this.grid.customEditors[n]];
53830         }
53831         if(typeof(this.grid.customEditors[n]) != 'undefined'){
53832             return this.grid.customEditors[n];
53833         }
53834         if(val instanceof Date){
53835             return this.editors['date'];
53836         }else if(typeof val == 'number'){
53837             return this.editors['number'];
53838         }else if(typeof val == 'boolean'){
53839             return this.editors['boolean'];
53840         }else{
53841             return this.editors['string'];
53842         }
53843     }
53844 });
53845
53846 /**
53847  * @class Roo.grid.PropertyGrid
53848  * @extends Roo.grid.EditorGrid
53849  * This class represents the  interface of a component based property grid control.
53850  * <br><br>Usage:<pre><code>
53851  var grid = new Roo.grid.PropertyGrid("my-container-id", {
53852       
53853  });
53854  // set any options
53855  grid.render();
53856  * </code></pre>
53857   
53858  * @constructor
53859  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
53860  * The container MUST have some type of size defined for the grid to fill. The container will be
53861  * automatically set to position relative if it isn't already.
53862  * @param {Object} config A config object that sets properties on this grid.
53863  */
53864 Roo.grid.PropertyGrid = function(container, config){
53865     config = config || {};
53866     var store = new Roo.grid.PropertyStore(this);
53867     this.store = store;
53868     var cm = new Roo.grid.PropertyColumnModel(this, store);
53869     store.store.sort('name', 'ASC');
53870     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
53871         ds: store.store,
53872         cm: cm,
53873         enableColLock:false,
53874         enableColumnMove:false,
53875         stripeRows:false,
53876         trackMouseOver: false,
53877         clicksToEdit:1
53878     }, config));
53879     this.getGridEl().addClass('x-props-grid');
53880     this.lastEditRow = null;
53881     this.on('columnresize', this.onColumnResize, this);
53882     this.addEvents({
53883          /**
53884              * @event beforepropertychange
53885              * Fires before a property changes (return false to stop?)
53886              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
53887              * @param {String} id Record Id
53888              * @param {String} newval New Value
53889          * @param {String} oldval Old Value
53890              */
53891         "beforepropertychange": true,
53892         /**
53893              * @event propertychange
53894              * Fires after a property changes
53895              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
53896              * @param {String} id Record Id
53897              * @param {String} newval New Value
53898          * @param {String} oldval Old Value
53899              */
53900         "propertychange": true
53901     });
53902     this.customEditors = this.customEditors || {};
53903 };
53904 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
53905     
53906      /**
53907      * @cfg {Object} customEditors map of colnames=> custom editors.
53908      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
53909      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
53910      * false disables editing of the field.
53911          */
53912     
53913       /**
53914      * @cfg {Object} propertyNames map of property Names to their displayed value
53915          */
53916     
53917     render : function(){
53918         Roo.grid.PropertyGrid.superclass.render.call(this);
53919         this.autoSize.defer(100, this);
53920     },
53921
53922     autoSize : function(){
53923         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
53924         if(this.view){
53925             this.view.fitColumns();
53926         }
53927     },
53928
53929     onColumnResize : function(){
53930         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
53931         this.autoSize();
53932     },
53933     /**
53934      * Sets the data for the Grid
53935      * accepts a Key => Value object of all the elements avaiable.
53936      * @param {Object} data  to appear in grid.
53937      */
53938     setSource : function(source){
53939         this.store.setSource(source);
53940         //this.autoSize();
53941     },
53942     /**
53943      * Gets all the data from the grid.
53944      * @return {Object} data  data stored in grid
53945      */
53946     getSource : function(){
53947         return this.store.getSource();
53948     }
53949 });/*
53950  * Based on:
53951  * Ext JS Library 1.1.1
53952  * Copyright(c) 2006-2007, Ext JS, LLC.
53953  *
53954  * Originally Released Under LGPL - original licence link has changed is not relivant.
53955  *
53956  * Fork - LGPL
53957  * <script type="text/javascript">
53958  */
53959  
53960 /**
53961  * @class Roo.LoadMask
53962  * A simple utility class for generically masking elements while loading data.  If the element being masked has
53963  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
53964  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
53965  * element's UpdateManager load indicator and will be destroyed after the initial load.
53966  * @constructor
53967  * Create a new LoadMask
53968  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
53969  * @param {Object} config The config object
53970  */
53971 Roo.LoadMask = function(el, config){
53972     this.el = Roo.get(el);
53973     Roo.apply(this, config);
53974     if(this.store){
53975         this.store.on('beforeload', this.onBeforeLoad, this);
53976         this.store.on('load', this.onLoad, this);
53977         this.store.on('loadexception', this.onLoadException, this);
53978         this.removeMask = false;
53979     }else{
53980         var um = this.el.getUpdateManager();
53981         um.showLoadIndicator = false; // disable the default indicator
53982         um.on('beforeupdate', this.onBeforeLoad, this);
53983         um.on('update', this.onLoad, this);
53984         um.on('failure', this.onLoad, this);
53985         this.removeMask = true;
53986     }
53987 };
53988
53989 Roo.LoadMask.prototype = {
53990     /**
53991      * @cfg {Boolean} removeMask
53992      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
53993      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
53994      */
53995     /**
53996      * @cfg {String} msg
53997      * The text to display in a centered loading message box (defaults to 'Loading...')
53998      */
53999     msg : 'Loading...',
54000     /**
54001      * @cfg {String} msgCls
54002      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
54003      */
54004     msgCls : 'x-mask-loading',
54005
54006     /**
54007      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
54008      * @type Boolean
54009      */
54010     disabled: false,
54011
54012     /**
54013      * Disables the mask to prevent it from being displayed
54014      */
54015     disable : function(){
54016        this.disabled = true;
54017     },
54018
54019     /**
54020      * Enables the mask so that it can be displayed
54021      */
54022     enable : function(){
54023         this.disabled = false;
54024     },
54025     
54026     onLoadException : function()
54027     {
54028         Roo.log(arguments);
54029         
54030         if (typeof(arguments[3]) != 'undefined') {
54031             Roo.MessageBox.alert("Error loading",arguments[3]);
54032         } 
54033         /*
54034         try {
54035             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
54036                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
54037             }   
54038         } catch(e) {
54039             
54040         }
54041         */
54042     
54043         
54044         
54045         this.el.unmask(this.removeMask);
54046     },
54047     // private
54048     onLoad : function()
54049     {
54050         this.el.unmask(this.removeMask);
54051     },
54052
54053     // private
54054     onBeforeLoad : function(){
54055         if(!this.disabled){
54056             this.el.mask(this.msg, this.msgCls);
54057         }
54058     },
54059
54060     // private
54061     destroy : function(){
54062         if(this.store){
54063             this.store.un('beforeload', this.onBeforeLoad, this);
54064             this.store.un('load', this.onLoad, this);
54065             this.store.un('loadexception', this.onLoadException, this);
54066         }else{
54067             var um = this.el.getUpdateManager();
54068             um.un('beforeupdate', this.onBeforeLoad, this);
54069             um.un('update', this.onLoad, this);
54070             um.un('failure', this.onLoad, this);
54071         }
54072     }
54073 };/*
54074  * Based on:
54075  * Ext JS Library 1.1.1
54076  * Copyright(c) 2006-2007, Ext JS, LLC.
54077  *
54078  * Originally Released Under LGPL - original licence link has changed is not relivant.
54079  *
54080  * Fork - LGPL
54081  * <script type="text/javascript">
54082  */
54083
54084
54085 /**
54086  * @class Roo.XTemplate
54087  * @extends Roo.Template
54088  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
54089 <pre><code>
54090 var t = new Roo.XTemplate(
54091         '&lt;select name="{name}"&gt;',
54092                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
54093         '&lt;/select&gt;'
54094 );
54095  
54096 // then append, applying the master template values
54097  </code></pre>
54098  *
54099  * Supported features:
54100  *
54101  *  Tags:
54102
54103 <pre><code>
54104       {a_variable} - output encoded.
54105       {a_variable.format:("Y-m-d")} - call a method on the variable
54106       {a_variable:raw} - unencoded output
54107       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
54108       {a_variable:this.method_on_template(...)} - call a method on the template object.
54109  
54110 </code></pre>
54111  *  The tpl tag:
54112 <pre><code>
54113         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
54114         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
54115         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
54116         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
54117   
54118         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
54119         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
54120 </code></pre>
54121  *      
54122  */
54123 Roo.XTemplate = function()
54124 {
54125     Roo.XTemplate.superclass.constructor.apply(this, arguments);
54126     if (this.html) {
54127         this.compile();
54128     }
54129 };
54130
54131
54132 Roo.extend(Roo.XTemplate, Roo.Template, {
54133
54134     /**
54135      * The various sub templates
54136      */
54137     tpls : false,
54138     /**
54139      *
54140      * basic tag replacing syntax
54141      * WORD:WORD()
54142      *
54143      * // you can fake an object call by doing this
54144      *  x.t:(test,tesT) 
54145      * 
54146      */
54147     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
54148
54149     /**
54150      * compile the template
54151      *
54152      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
54153      *
54154      */
54155     compile: function()
54156     {
54157         var s = this.html;
54158      
54159         s = ['<tpl>', s, '</tpl>'].join('');
54160     
54161         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
54162             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
54163             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
54164             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
54165             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
54166             m,
54167             id     = 0,
54168             tpls   = [];
54169     
54170         while(true == !!(m = s.match(re))){
54171             var forMatch   = m[0].match(nameRe),
54172                 ifMatch   = m[0].match(ifRe),
54173                 execMatch   = m[0].match(execRe),
54174                 namedMatch   = m[0].match(namedRe),
54175                 
54176                 exp  = null, 
54177                 fn   = null,
54178                 exec = null,
54179                 name = forMatch && forMatch[1] ? forMatch[1] : '';
54180                 
54181             if (ifMatch) {
54182                 // if - puts fn into test..
54183                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
54184                 if(exp){
54185                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
54186                 }
54187             }
54188             
54189             if (execMatch) {
54190                 // exec - calls a function... returns empty if true is  returned.
54191                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
54192                 if(exp){
54193                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
54194                 }
54195             }
54196             
54197             
54198             if (name) {
54199                 // for = 
54200                 switch(name){
54201                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
54202                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
54203                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
54204                 }
54205             }
54206             var uid = namedMatch ? namedMatch[1] : id;
54207             
54208             
54209             tpls.push({
54210                 id:     namedMatch ? namedMatch[1] : id,
54211                 target: name,
54212                 exec:   exec,
54213                 test:   fn,
54214                 body:   m[1] || ''
54215             });
54216             if (namedMatch) {
54217                 s = s.replace(m[0], '');
54218             } else { 
54219                 s = s.replace(m[0], '{xtpl'+ id + '}');
54220             }
54221             ++id;
54222         }
54223         this.tpls = [];
54224         for(var i = tpls.length-1; i >= 0; --i){
54225             this.compileTpl(tpls[i]);
54226             this.tpls[tpls[i].id] = tpls[i];
54227         }
54228         this.master = tpls[tpls.length-1];
54229         return this;
54230     },
54231     /**
54232      * same as applyTemplate, except it's done to one of the subTemplates
54233      * when using named templates, you can do:
54234      *
54235      * var str = pl.applySubTemplate('your-name', values);
54236      *
54237      * 
54238      * @param {Number} id of the template
54239      * @param {Object} values to apply to template
54240      * @param {Object} parent (normaly the instance of this object)
54241      */
54242     applySubTemplate : function(id, values, parent)
54243     {
54244         
54245         
54246         var t = this.tpls[id];
54247         
54248         
54249         try { 
54250             if(t.test && !t.test.call(this, values, parent)){
54251                 return '';
54252             }
54253         } catch(e) {
54254             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
54255             Roo.log(e.toString());
54256             Roo.log(t.test);
54257             return ''
54258         }
54259         try { 
54260             
54261             if(t.exec && t.exec.call(this, values, parent)){
54262                 return '';
54263             }
54264         } catch(e) {
54265             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
54266             Roo.log(e.toString());
54267             Roo.log(t.exec);
54268             return ''
54269         }
54270         try {
54271             var vs = t.target ? t.target.call(this, values, parent) : values;
54272             parent = t.target ? values : parent;
54273             if(t.target && vs instanceof Array){
54274                 var buf = [];
54275                 for(var i = 0, len = vs.length; i < len; i++){
54276                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
54277                 }
54278                 return buf.join('');
54279             }
54280             return t.compiled.call(this, vs, parent);
54281         } catch (e) {
54282             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
54283             Roo.log(e.toString());
54284             Roo.log(t.compiled);
54285             return '';
54286         }
54287     },
54288
54289     compileTpl : function(tpl)
54290     {
54291         var fm = Roo.util.Format;
54292         var useF = this.disableFormats !== true;
54293         var sep = Roo.isGecko ? "+" : ",";
54294         var undef = function(str) {
54295             Roo.log("Property not found :"  + str);
54296             return '';
54297         };
54298         
54299         var fn = function(m, name, format, args)
54300         {
54301             //Roo.log(arguments);
54302             args = args ? args.replace(/\\'/g,"'") : args;
54303             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
54304             if (typeof(format) == 'undefined') {
54305                 format= 'htmlEncode';
54306             }
54307             if (format == 'raw' ) {
54308                 format = false;
54309             }
54310             
54311             if(name.substr(0, 4) == 'xtpl'){
54312                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
54313             }
54314             
54315             // build an array of options to determine if value is undefined..
54316             
54317             // basically get 'xxxx.yyyy' then do
54318             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
54319             //    (function () { Roo.log("Property not found"); return ''; })() :
54320             //    ......
54321             
54322             var udef_ar = [];
54323             var lookfor = '';
54324             Roo.each(name.split('.'), function(st) {
54325                 lookfor += (lookfor.length ? '.': '') + st;
54326                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
54327             });
54328             
54329             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
54330             
54331             
54332             if(format && useF){
54333                 
54334                 args = args ? ',' + args : "";
54335                  
54336                 if(format.substr(0, 5) != "this."){
54337                     format = "fm." + format + '(';
54338                 }else{
54339                     format = 'this.call("'+ format.substr(5) + '", ';
54340                     args = ", values";
54341                 }
54342                 
54343                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
54344             }
54345              
54346             if (args.length) {
54347                 // called with xxyx.yuu:(test,test)
54348                 // change to ()
54349                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
54350             }
54351             // raw.. - :raw modifier..
54352             return "'"+ sep + udef_st  + name + ")"+sep+"'";
54353             
54354         };
54355         var body;
54356         // branched to use + in gecko and [].join() in others
54357         if(Roo.isGecko){
54358             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
54359                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
54360                     "';};};";
54361         }else{
54362             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
54363             body.push(tpl.body.replace(/(\r\n|\n)/g,
54364                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
54365             body.push("'].join('');};};");
54366             body = body.join('');
54367         }
54368         
54369         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
54370        
54371         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
54372         eval(body);
54373         
54374         return this;
54375     },
54376
54377     applyTemplate : function(values){
54378         return this.master.compiled.call(this, values, {});
54379         //var s = this.subs;
54380     },
54381
54382     apply : function(){
54383         return this.applyTemplate.apply(this, arguments);
54384     }
54385
54386  });
54387
54388 Roo.XTemplate.from = function(el){
54389     el = Roo.getDom(el);
54390     return new Roo.XTemplate(el.value || el.innerHTML);
54391 };/*
54392  * Original code for Roojs - LGPL
54393  * <script type="text/javascript">
54394  */
54395  
54396 /**
54397  * @class Roo.XComponent
54398  * A delayed Element creator...
54399  * Or a way to group chunks of interface together.
54400  * 
54401  * Mypart.xyx = new Roo.XComponent({
54402
54403     parent : 'Mypart.xyz', // empty == document.element.!!
54404     order : '001',
54405     name : 'xxxx'
54406     region : 'xxxx'
54407     disabled : function() {} 
54408      
54409     tree : function() { // return an tree of xtype declared components
54410         var MODULE = this;
54411         return 
54412         {
54413             xtype : 'NestedLayoutPanel',
54414             // technicall
54415         }
54416      ]
54417  *})
54418  *
54419  *
54420  * It can be used to build a big heiracy, with parent etc.
54421  * or you can just use this to render a single compoent to a dom element
54422  * MYPART.render(Roo.Element | String(id) | dom_element )
54423  * 
54424  * @extends Roo.util.Observable
54425  * @constructor
54426  * @param cfg {Object} configuration of component
54427  * 
54428  */
54429 Roo.XComponent = function(cfg) {
54430     Roo.apply(this, cfg);
54431     this.addEvents({ 
54432         /**
54433              * @event built
54434              * Fires when this the componnt is built
54435              * @param {Roo.XComponent} c the component
54436              */
54437         'built' : true
54438         
54439     });
54440     this.region = this.region || 'center'; // default..
54441     Roo.XComponent.register(this);
54442     this.modules = false;
54443     this.el = false; // where the layout goes..
54444     
54445     
54446 }
54447 Roo.extend(Roo.XComponent, Roo.util.Observable, {
54448     /**
54449      * @property el
54450      * The created element (with Roo.factory())
54451      * @type {Roo.Layout}
54452      */
54453     el  : false,
54454     
54455     /**
54456      * @property el
54457      * for BC  - use el in new code
54458      * @type {Roo.Layout}
54459      */
54460     panel : false,
54461     
54462     /**
54463      * @property layout
54464      * for BC  - use el in new code
54465      * @type {Roo.Layout}
54466      */
54467     layout : false,
54468     
54469      /**
54470      * @cfg {Function|boolean} disabled
54471      * If this module is disabled by some rule, return true from the funtion
54472      */
54473     disabled : false,
54474     
54475     /**
54476      * @cfg {String} parent 
54477      * Name of parent element which it get xtype added to..
54478      */
54479     parent: false,
54480     
54481     /**
54482      * @cfg {String} order
54483      * Used to set the order in which elements are created (usefull for multiple tabs)
54484      */
54485     
54486     order : false,
54487     /**
54488      * @cfg {String} name
54489      * String to display while loading.
54490      */
54491     name : false,
54492     /**
54493      * @cfg {String} region
54494      * Region to render component to (defaults to center)
54495      */
54496     region : 'center',
54497     
54498     /**
54499      * @cfg {Array} items
54500      * A single item array - the first element is the root of the tree..
54501      * It's done this way to stay compatible with the Xtype system...
54502      */
54503     items : false,
54504     
54505     /**
54506      * @property _tree
54507      * The method that retuns the tree of parts that make up this compoennt 
54508      * @type {function}
54509      */
54510     _tree  : false,
54511     
54512      /**
54513      * render
54514      * render element to dom or tree
54515      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
54516      */
54517     
54518     render : function(el)
54519     {
54520         
54521         el = el || false;
54522         var hp = this.parent ? 1 : 0;
54523         
54524         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
54525             // if parent is a '#.....' string, then let's use that..
54526             var ename = this.parent.substr(1)
54527             this.parent = false;
54528             el = Roo.get(ename);
54529             if (!el) {
54530                 Roo.log("Warning - element can not be found :#" + ename );
54531                 return;
54532             }
54533         }
54534         
54535         
54536         if (!this.parent) {
54537             
54538             el = el ? Roo.get(el) : false;      
54539             
54540             // it's a top level one..
54541             this.parent =  {
54542                 el : new Roo.BorderLayout(el || document.body, {
54543                 
54544                      center: {
54545                          titlebar: false,
54546                          autoScroll:false,
54547                          closeOnTab: true,
54548                          tabPosition: 'top',
54549                           //resizeTabs: true,
54550                          alwaysShowTabs: el && hp? false :  true,
54551                          hideTabs: el || !hp ? true :  false,
54552                          minTabWidth: 140
54553                      }
54554                  })
54555             }
54556         }
54557         
54558                 if (!this.parent.el) {
54559                         // probably an old style ctor, which has been disabled.
54560                         return;
54561                         
54562                 }
54563                 // The 'tree' method is  '_tree now' 
54564             
54565         var tree = this._tree ? this._tree() : this.tree();
54566         tree.region = tree.region || this.region;
54567         this.el = this.parent.el.addxtype(tree);
54568         this.fireEvent('built', this);
54569         
54570         this.panel = this.el;
54571         this.layout = this.panel.layout;
54572                 this.parentLayout = this.parent.layout  || false;  
54573          
54574     }
54575     
54576 });
54577
54578 Roo.apply(Roo.XComponent, {
54579     /**
54580      * @property  hideProgress
54581      * true to disable the building progress bar.. usefull on single page renders.
54582      * @type Boolean
54583      */
54584     hideProgress : false,
54585     /**
54586      * @property  buildCompleted
54587      * True when the builder has completed building the interface.
54588      * @type Boolean
54589      */
54590     buildCompleted : false,
54591      
54592     /**
54593      * @property  topModule
54594      * the upper most module - uses document.element as it's constructor.
54595      * @type Object
54596      */
54597      
54598     topModule  : false,
54599       
54600     /**
54601      * @property  modules
54602      * array of modules to be created by registration system.
54603      * @type {Array} of Roo.XComponent
54604      */
54605     
54606     modules : [],
54607     /**
54608      * @property  elmodules
54609      * array of modules to be created by which use #ID 
54610      * @type {Array} of Roo.XComponent
54611      */
54612      
54613     elmodules : [],
54614
54615     
54616     /**
54617      * Register components to be built later.
54618      *
54619      * This solves the following issues
54620      * - Building is not done on page load, but after an authentication process has occured.
54621      * - Interface elements are registered on page load
54622      * - Parent Interface elements may not be loaded before child, so this handles that..
54623      * 
54624      *
54625      * example:
54626      * 
54627      * MyApp.register({
54628           order : '000001',
54629           module : 'Pman.Tab.projectMgr',
54630           region : 'center',
54631           parent : 'Pman.layout',
54632           disabled : false,  // or use a function..
54633         })
54634      
54635      * * @param {Object} details about module
54636      */
54637     register : function(obj) {
54638                 
54639         Roo.XComponent.event.fireEvent('register', obj);
54640         switch(typeof(obj.disabled) ) {
54641                 
54642             case 'undefined':
54643                 break;
54644             
54645             case 'function':
54646                 if ( obj.disabled() ) {
54647                         return;
54648                 }
54649                 break;
54650             
54651             default:
54652                 if (obj.disabled) {
54653                         return;
54654                 }
54655                 break;
54656         }
54657                 
54658         this.modules.push(obj);
54659          
54660     },
54661     /**
54662      * convert a string to an object..
54663      * eg. 'AAA.BBB' -> finds AAA.BBB
54664
54665      */
54666     
54667     toObject : function(str)
54668     {
54669         if (!str || typeof(str) == 'object') {
54670             return str;
54671         }
54672         if (str.substring(0,1) == '#') {
54673             return str;
54674         }
54675
54676         var ar = str.split('.');
54677         var rt, o;
54678         rt = ar.shift();
54679             /** eval:var:o */
54680         try {
54681             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
54682         } catch (e) {
54683             throw "Module not found : " + str;
54684         }
54685         
54686         if (o === false) {
54687             throw "Module not found : " + str;
54688         }
54689         Roo.each(ar, function(e) {
54690             if (typeof(o[e]) == 'undefined') {
54691                 throw "Module not found : " + str;
54692             }
54693             o = o[e];
54694         });
54695         
54696         return o;
54697         
54698     },
54699     
54700     
54701     /**
54702      * move modules into their correct place in the tree..
54703      * 
54704      */
54705     preBuild : function ()
54706     {
54707         var _t = this;
54708         Roo.each(this.modules , function (obj)
54709         {
54710             Roo.XComponent.event.fireEvent('beforebuild', obj);
54711             
54712             var opar = obj.parent;
54713             try { 
54714                 obj.parent = this.toObject(opar);
54715             } catch(e) {
54716                 Roo.log("parent:toObject failed: " + e.toString());
54717                 return;
54718             }
54719             
54720             if (!obj.parent) {
54721                 Roo.debug && Roo.log("GOT top level module");
54722                 Roo.debug && Roo.log(obj);
54723                 obj.modules = new Roo.util.MixedCollection(false, 
54724                     function(o) { return o.order + '' }
54725                 );
54726                 this.topModule = obj;
54727                 return;
54728             }
54729                         // parent is a string (usually a dom element name..)
54730             if (typeof(obj.parent) == 'string') {
54731                 this.elmodules.push(obj);
54732                 return;
54733             }
54734             if (obj.parent.constructor != Roo.XComponent) {
54735                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
54736             }
54737             if (!obj.parent.modules) {
54738                 obj.parent.modules = new Roo.util.MixedCollection(false, 
54739                     function(o) { return o.order + '' }
54740                 );
54741             }
54742             if (obj.parent.disabled) {
54743                 obj.disabled = true;
54744             }
54745             obj.parent.modules.add(obj);
54746         }, this);
54747     },
54748     
54749      /**
54750      * make a list of modules to build.
54751      * @return {Array} list of modules. 
54752      */ 
54753     
54754     buildOrder : function()
54755     {
54756         var _this = this;
54757         var cmp = function(a,b) {   
54758             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
54759         };
54760         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
54761             throw "No top level modules to build";
54762         }
54763         
54764         // make a flat list in order of modules to build.
54765         var mods = this.topModule ? [ this.topModule ] : [];
54766                 
54767         // elmodules (is a list of DOM based modules )
54768         Roo.each(this.elmodules, function(e) {
54769             mods.push(e)
54770         });
54771
54772         
54773         // add modules to their parents..
54774         var addMod = function(m) {
54775             Roo.debug && Roo.log("build Order: add: " + m.name);
54776             
54777         mods.push(m);
54778         if (m.modules && !m.disabled) {
54779             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
54780             m.modules.keySort('ASC',  cmp );
54781             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
54782
54783             m.modules.each(addMod);
54784         } else {
54785             Roo.debug && Roo.log("build Order: no child modules");
54786             }
54787             // not sure if this is used any more..
54788             if (m.finalize) {
54789                 m.finalize.name = m.name + " (clean up) ";
54790                 mods.push(m.finalize);
54791             }
54792             
54793         }
54794         if (this.topModule) { 
54795             this.topModule.modules.keySort('ASC',  cmp );
54796             this.topModule.modules.each(addMod);
54797         }
54798         return mods;
54799     },
54800     
54801      /**
54802      * Build the registered modules.
54803      * @param {Object} parent element.
54804      * @param {Function} optional method to call after module has been added.
54805      * 
54806      */ 
54807    
54808     build : function() 
54809     {
54810         
54811         this.preBuild();
54812         var mods = this.buildOrder();
54813       
54814         //this.allmods = mods;
54815         //Roo.debug && Roo.log(mods);
54816         //return;
54817         if (!mods.length) { // should not happen
54818             throw "NO modules!!!";
54819         }
54820         
54821         
54822         var msg = "Building Interface...";
54823         // flash it up as modal - so we store the mask!?
54824         if (!this.hideProgress) {
54825             Roo.MessageBox.show({ title: 'loading' });
54826             Roo.MessageBox.show({
54827                title: "Please wait...",
54828                msg: msg,
54829                width:450,
54830                progress:true,
54831                closable:false,
54832                modal: false
54833               
54834             });
54835         }
54836         var total = mods.length;
54837         
54838         var _this = this;
54839         var progressRun = function() {
54840             if (!mods.length) {
54841                 Roo.debug && Roo.log('hide?');
54842                 if (!this.hideProgress) {
54843                     Roo.MessageBox.hide();
54844                 }
54845                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
54846                 
54847                 // THE END...
54848                 return false;   
54849             }
54850             
54851             var m = mods.shift();
54852             
54853             
54854             Roo.debug && Roo.log(m);
54855             // not sure if this is supported any more.. - modules that are are just function
54856             if (typeof(m) == 'function') { 
54857                 m.call(this);
54858                 return progressRun.defer(10, _this);
54859             } 
54860             
54861             
54862             msg = "Building Interface " + (total  - mods.length) + 
54863                     " of " + total + 
54864                     (m.name ? (' - ' + m.name) : '');
54865                         Roo.debug && Roo.log(msg);
54866             if (!this.hideProgress) { 
54867                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
54868             }
54869             
54870          
54871             // is the module disabled?
54872             var disabled = (typeof(m.disabled) == 'function') ?
54873                 m.disabled.call(m.module.disabled) : m.disabled;    
54874             
54875             
54876             if (disabled) {
54877                 return progressRun(); // we do not update the display!
54878             }
54879             
54880             // now build 
54881             
54882                         
54883                         
54884             m.render();
54885             // it's 10 on top level, and 1 on others??? why...
54886             return progressRun.defer(10, _this);
54887              
54888         }
54889         progressRun.defer(1, _this);
54890      
54891         
54892         
54893     },
54894         
54895         
54896         /**
54897          * Event Object.
54898          *
54899          *
54900          */
54901         event: false, 
54902     /**
54903          * wrapper for event.on - aliased later..  
54904          * Typically use to register a event handler for register:
54905          *
54906          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
54907          *
54908          */
54909     on : false
54910    
54911     
54912     
54913 });
54914
54915 Roo.XComponent.event = new Roo.util.Observable({
54916                 events : { 
54917                         /**
54918                          * @event register
54919                          * Fires when an Component is registered,
54920                          * set the disable property on the Component to stop registration.
54921                          * @param {Roo.XComponent} c the component being registerd.
54922                          * 
54923                          */
54924                         'register' : true,
54925             /**
54926                          * @event beforebuild
54927                          * Fires before each Component is built
54928                          * can be used to apply permissions.
54929                          * @param {Roo.XComponent} c the component being registerd.
54930                          * 
54931                          */
54932                         'beforebuild' : true,
54933                         /**
54934                          * @event buildcomplete
54935                          * Fires on the top level element when all elements have been built
54936                          * @param {Roo.XComponent} the top level component.
54937                          */
54938                         'buildcomplete' : true
54939                         
54940                 }
54941 });
54942
54943 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
54944  //<script type="text/javascript">
54945
54946
54947 /**
54948  * @class Roo.Login
54949  * @extends Roo.LayoutDialog
54950  * A generic Login Dialog..... - only one needed in theory!?!?
54951  *
54952  * Fires XComponent builder on success...
54953  * 
54954  * Sends 
54955  *    username,password, lang = for login actions.
54956  *    check = 1 for periodic checking that sesion is valid.
54957  *    passwordRequest = email request password
54958  *    logout = 1 = to logout
54959  * 
54960  * Affects: (this id="????" elements)
54961  *   loading  (removed) (used to indicate application is loading)
54962  *   loading-mask (hides) (used to hide application when it's building loading)
54963  *   
54964  * 
54965  * Usage: 
54966  *    
54967  * 
54968  * Myapp.login = Roo.Login({
54969      url: xxxx,
54970    
54971      realm : 'Myapp', 
54972      
54973      
54974      method : 'POST',
54975      
54976      
54977      * 
54978  })
54979  * 
54980  * 
54981  * 
54982  **/
54983  
54984 Roo.Login = function(cfg)
54985 {
54986     this.addEvents({
54987         'refreshed' : true
54988     });
54989     
54990     Roo.apply(this,cfg);
54991     
54992     Roo.onReady(function() {
54993         this.onLoad();
54994     }, this);
54995     // call parent..
54996     
54997    
54998     Roo.Login.superclass.constructor.call(this, this);
54999     //this.addxtype(this.items[0]);
55000     
55001     
55002 }
55003
55004
55005 Roo.extend(Roo.Login, Roo.LayoutDialog, {
55006     
55007     /**
55008      * @cfg {String} method
55009      * Method used to query for login details.
55010      */
55011     
55012     method : 'POST',
55013     /**
55014      * @cfg {String} url
55015      * URL to query login data. - eg. baseURL + '/Login.php'
55016      */
55017     url : '',
55018     
55019     /**
55020      * @property user
55021      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
55022      * @type {Object} 
55023      */
55024     user : false,
55025     /**
55026      * @property checkFails
55027      * Number of times we have attempted to get authentication check, and failed.
55028      * @type {Number} 
55029      */
55030     checkFails : 0,
55031       /**
55032      * @property intervalID
55033      * The window interval that does the constant login checking.
55034      * @type {Number} 
55035      */
55036     intervalID : 0,
55037     
55038     
55039     onLoad : function() // called on page load...
55040     {
55041         // load 
55042          
55043         if (Roo.get('loading')) { // clear any loading indicator..
55044             Roo.get('loading').remove();
55045         }
55046         
55047         //this.switchLang('en'); // set the language to english..
55048        
55049         this.check({
55050             success:  function(response, opts)  {  // check successfull...
55051             
55052                 var res = this.processResponse(response);
55053                 this.checkFails =0;
55054                 if (!res.success) { // error!
55055                     this.checkFails = 5;
55056                     //console.log('call failure');
55057                     return this.failure(response,opts);
55058                 }
55059                 
55060                 if (!res.data.id) { // id=0 == login failure.
55061                     return this.show();
55062                 }
55063                 
55064                               
55065                         //console.log(success);
55066                 this.fillAuth(res.data);   
55067                 this.checkFails =0;
55068                 Roo.XComponent.build();
55069             },
55070             failure : this.show
55071         });
55072         
55073     }, 
55074     
55075     
55076     check: function(cfg) // called every so often to refresh cookie etc..
55077     {
55078         if (cfg.again) { // could be undefined..
55079             this.checkFails++;
55080         } else {
55081             this.checkFails = 0;
55082         }
55083         var _this = this;
55084         if (this.sending) {
55085             if ( this.checkFails > 4) {
55086                 Roo.MessageBox.alert("Error",  
55087                     "Error getting authentication status. - try reloading, or wait a while", function() {
55088                         _this.sending = false;
55089                     }); 
55090                 return;
55091             }
55092             cfg.again = true;
55093             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
55094             return;
55095         }
55096         this.sending = true;
55097         
55098         Roo.Ajax.request({  
55099             url: this.url,
55100             params: {
55101                 getAuthUser: true
55102             },  
55103             method: this.method,
55104             success:  cfg.success || this.success,
55105             failure : cfg.failure || this.failure,
55106             scope : this,
55107             callCfg : cfg
55108               
55109         });  
55110     }, 
55111     
55112     
55113     logout: function()
55114     {
55115         window.onbeforeunload = function() { }; // false does not work for IE..
55116         this.user = false;
55117         var _this = this;
55118         
55119         Roo.Ajax.request({  
55120             url: this.url,
55121             params: {
55122                 logout: 1
55123             },  
55124             method: 'GET',
55125             failure : function() {
55126                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
55127                     document.location = document.location.toString() + '?ts=' + Math.random();
55128                 });
55129                 
55130             },
55131             success : function() {
55132                 _this.user = false;
55133                 this.checkFails =0;
55134                 // fixme..
55135                 document.location = document.location.toString() + '?ts=' + Math.random();
55136             }
55137               
55138               
55139         }); 
55140     },
55141     
55142     processResponse : function (response)
55143     {
55144         var res = '';
55145         try {
55146             res = Roo.decode(response.responseText);
55147             // oops...
55148             if (typeof(res) != 'object') {
55149                 res = { success : false, errorMsg : res, errors : true };
55150             }
55151             if (typeof(res.success) == 'undefined') {
55152                 res.success = false;
55153             }
55154             
55155         } catch(e) {
55156             res = { success : false,  errorMsg : response.responseText, errors : true };
55157         }
55158         return res;
55159     },
55160     
55161     success : function(response, opts)  // check successfull...
55162     {  
55163         this.sending = false;
55164         var res = this.processResponse(response);
55165         if (!res.success) {
55166             return this.failure(response, opts);
55167         }
55168         if (!res.data || !res.data.id) {
55169             return this.failure(response,opts);
55170         }
55171         //console.log(res);
55172         this.fillAuth(res.data);
55173         
55174         this.checkFails =0;
55175         
55176     },
55177     
55178     
55179     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
55180     {
55181         this.authUser = -1;
55182         this.sending = false;
55183         var res = this.processResponse(response);
55184         //console.log(res);
55185         if ( this.checkFails > 2) {
55186         
55187             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
55188                 "Error getting authentication status. - try reloading"); 
55189             return;
55190         }
55191         opts.callCfg.again = true;
55192         this.check.defer(1000, this, [ opts.callCfg ]);
55193         return;  
55194     },
55195     
55196     
55197     
55198     fillAuth: function(au) {
55199         this.startAuthCheck();
55200         this.authUserId = au.id;
55201         this.authUser = au;
55202         this.lastChecked = new Date();
55203         this.fireEvent('refreshed', au);
55204         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
55205         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
55206         au.lang = au.lang || 'en';
55207         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
55208         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
55209         this.switchLang(au.lang );
55210         
55211      
55212         // open system... - -on setyp..
55213         if (this.authUserId  < 0) {
55214             Roo.MessageBox.alert("Warning", 
55215                 "This is an open system - please set up a admin user with a password.");  
55216         }
55217          
55218         //Pman.onload(); // which should do nothing if it's a re-auth result...
55219         
55220              
55221     },
55222     
55223     startAuthCheck : function() // starter for timeout checking..
55224     {
55225         if (this.intervalID) { // timer already in place...
55226             return false;
55227         }
55228         var _this = this;
55229         this.intervalID =  window.setInterval(function() {
55230               _this.check(false);
55231             }, 120000); // every 120 secs = 2mins..
55232         
55233         
55234     },
55235          
55236     
55237     switchLang : function (lang) 
55238     {
55239         _T = typeof(_T) == 'undefined' ? false : _T;
55240           if (!_T || !lang.length) {
55241             return;
55242         }
55243         
55244         if (!_T && lang != 'en') {
55245             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
55246             return;
55247         }
55248         
55249         if (typeof(_T.en) == 'undefined') {
55250             _T.en = {};
55251             Roo.apply(_T.en, _T);
55252         }
55253         
55254         if (typeof(_T[lang]) == 'undefined') {
55255             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
55256             return;
55257         }
55258         
55259         
55260         Roo.apply(_T, _T[lang]);
55261         // just need to set the text values for everything...
55262         var _this = this;
55263         /* this will not work ...
55264         if (this.form) { 
55265             
55266                
55267             function formLabel(name, val) {
55268                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
55269             }
55270             
55271             formLabel('password', "Password"+':');
55272             formLabel('username', "Email Address"+':');
55273             formLabel('lang', "Language"+':');
55274             this.dialog.setTitle("Login");
55275             this.dialog.buttons[0].setText("Forgot Password");
55276             this.dialog.buttons[1].setText("Login");
55277         }
55278         */
55279         
55280         
55281     },
55282     
55283     
55284     title: "Login",
55285     modal: true,
55286     width:  350,
55287     //height: 230,
55288     height: 180,
55289     shadow: true,
55290     minWidth:200,
55291     minHeight:180,
55292     //proxyDrag: true,
55293     closable: false,
55294     draggable: false,
55295     collapsible: false,
55296     resizable: false,
55297     center: {  // needed??
55298         autoScroll:false,
55299         titlebar: false,
55300        // tabPosition: 'top',
55301         hideTabs: true,
55302         closeOnTab: true,
55303         alwaysShowTabs: false
55304     } ,
55305     listeners : {
55306         
55307         show  : function(dlg)
55308         {
55309             //console.log(this);
55310             this.form = this.layout.getRegion('center').activePanel.form;
55311             this.form.dialog = dlg;
55312             this.buttons[0].form = this.form;
55313             this.buttons[0].dialog = dlg;
55314             this.buttons[1].form = this.form;
55315             this.buttons[1].dialog = dlg;
55316            
55317            //this.resizeToLogo.defer(1000,this);
55318             // this is all related to resizing for logos..
55319             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
55320            //// if (!sz) {
55321              //   this.resizeToLogo.defer(1000,this);
55322              //   return;
55323            // }
55324             //var w = Ext.lib.Dom.getViewWidth() - 100;
55325             //var h = Ext.lib.Dom.getViewHeight() - 100;
55326             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
55327             //this.center();
55328             if (this.disabled) {
55329                 this.hide();
55330                 return;
55331             }
55332             
55333             if (this.user.id < 0) { // used for inital setup situations.
55334                 return;
55335             }
55336             
55337             if (this.intervalID) {
55338                 // remove the timer
55339                 window.clearInterval(this.intervalID);
55340                 this.intervalID = false;
55341             }
55342             
55343             
55344             if (Roo.get('loading')) {
55345                 Roo.get('loading').remove();
55346             }
55347             if (Roo.get('loading-mask')) {
55348                 Roo.get('loading-mask').hide();
55349             }
55350             
55351             //incomming._node = tnode;
55352             this.form.reset();
55353             //this.dialog.modal = !modal;
55354             //this.dialog.show();
55355             this.el.unmask(); 
55356             
55357             
55358             this.form.setValues({
55359                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
55360                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
55361             });
55362             
55363             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
55364             if (this.form.findField('username').getValue().length > 0 ){
55365                 this.form.findField('password').focus();
55366             } else {
55367                this.form.findField('username').focus();
55368             }
55369     
55370         }
55371     },
55372     items : [
55373          {
55374        
55375             xtype : 'ContentPanel',
55376             xns : Roo,
55377             region: 'center',
55378             fitToFrame : true,
55379             
55380             items : [
55381     
55382                 {
55383                
55384                     xtype : 'Form',
55385                     xns : Roo.form,
55386                     labelWidth: 100,
55387                     style : 'margin: 10px;',
55388                     
55389                     listeners : {
55390                         actionfailed : function(f, act) {
55391                             // form can return { errors: .... }
55392                                 
55393                             //act.result.errors // invalid form element list...
55394                             //act.result.errorMsg// invalid form element list...
55395                             
55396                             this.dialog.el.unmask();
55397                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
55398                                         "Login failed - communication error - try again.");
55399                                       
55400                         },
55401                         actioncomplete: function(re, act) {
55402                              
55403                             Roo.state.Manager.set(
55404                                 this.dialog.realm + '.username',  
55405                                     this.findField('username').getValue()
55406                             );
55407                             Roo.state.Manager.set(
55408                                 this.dialog.realm + '.lang',  
55409                                 this.findField('lang').getValue() 
55410                             );
55411                             
55412                             this.dialog.fillAuth(act.result.data);
55413                               
55414                             this.dialog.hide();
55415                             
55416                             if (Roo.get('loading-mask')) {
55417                                 Roo.get('loading-mask').show();
55418                             }
55419                             Roo.XComponent.build();
55420                             
55421                              
55422                             
55423                         }
55424                     },
55425                     items : [
55426                         {
55427                             xtype : 'TextField',
55428                             xns : Roo.form,
55429                             fieldLabel: "Email Address",
55430                             name: 'username',
55431                             width:200,
55432                             autoCreate : {tag: "input", type: "text", size: "20"}
55433                         },
55434                         {
55435                             xtype : 'TextField',
55436                             xns : Roo.form,
55437                             fieldLabel: "Password",
55438                             inputType: 'password',
55439                             name: 'password',
55440                             width:200,
55441                             autoCreate : {tag: "input", type: "text", size: "20"},
55442                             listeners : {
55443                                 specialkey : function(e,ev) {
55444                                     if (ev.keyCode == 13) {
55445                                         this.form.dialog.el.mask("Logging in");
55446                                         this.form.doAction('submit', {
55447                                             url: this.form.dialog.url,
55448                                             method: this.form.dialog.method
55449                                         });
55450                                     }
55451                                 }
55452                             }  
55453                         },
55454                         {
55455                             xtype : 'ComboBox',
55456                             xns : Roo.form,
55457                             fieldLabel: "Language",
55458                             name : 'langdisp',
55459                             store: {
55460                                 xtype : 'SimpleStore',
55461                                 fields: ['lang', 'ldisp'],
55462                                 data : [
55463                                     [ 'en', 'English' ],
55464                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
55465                                     [ 'zh_CN', '\u7C21\u4E2D' ]
55466                                 ]
55467                             },
55468                             
55469                             valueField : 'lang',
55470                             hiddenName:  'lang',
55471                             width: 200,
55472                             displayField:'ldisp',
55473                             typeAhead: false,
55474                             editable: false,
55475                             mode: 'local',
55476                             triggerAction: 'all',
55477                             emptyText:'Select a Language...',
55478                             selectOnFocus:true,
55479                             listeners : {
55480                                 select :  function(cb, rec, ix) {
55481                                     this.form.switchLang(rec.data.lang);
55482                                 }
55483                             }
55484                         
55485                         }
55486                     ]
55487                 }
55488                   
55489                 
55490             ]
55491         }
55492     ],
55493     buttons : [
55494         {
55495             xtype : 'Button',
55496             xns : 'Roo',
55497             text : "Forgot Password",
55498             listeners : {
55499                 click : function() {
55500                     //console.log(this);
55501                     var n = this.form.findField('username').getValue();
55502                     if (!n.length) {
55503                         Roo.MessageBox.alert("Error", "Fill in your email address");
55504                         return;
55505                     }
55506                     Roo.Ajax.request({
55507                         url: this.dialog.url,
55508                         params: {
55509                             passwordRequest: n
55510                         },
55511                         method: this.dialog.method,
55512                         success:  function(response, opts)  {  // check successfull...
55513                         
55514                             var res = this.dialog.processResponse(response);
55515                             if (!res.success) { // error!
55516                                Roo.MessageBox.alert("Error" ,
55517                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
55518                                return;
55519                             }
55520                             Roo.MessageBox.alert("Notice" ,
55521                                 "Please check you email for the Password Reset message");
55522                         },
55523                         failure : function() {
55524                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
55525                         }
55526                         
55527                     });
55528                 }
55529             }
55530         },
55531         {
55532             xtype : 'Button',
55533             xns : 'Roo',
55534             text : "Login",
55535             listeners : {
55536                 
55537                 click : function () {
55538                         
55539                     this.dialog.el.mask("Logging in");
55540                     this.form.doAction('submit', {
55541                             url: this.dialog.url,
55542                             method: this.dialog.method
55543                     });
55544                 }
55545             }
55546         }
55547     ]
55548   
55549   
55550 })
55551  
55552
55553
55554