roojs-ui-debug.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                 this.timeout[o.tId] = window.setTimeout(function() {
2714                     oConn.abort(o, callback, true);
2715                 }, callback.timeout);
2716             }
2717
2718             this.poll[o.tId] = window.setInterval(
2719                     function() {
2720                         if (o.conn && o.conn.readyState == 4) {
2721                             window.clearInterval(oConn.poll[o.tId]);
2722                             delete oConn.poll[o.tId];
2723
2724                             if(callback && callback.timeout) {
2725                                 window.clearTimeout(oConn.timeout[o.tId]);
2726                                 delete oConn.timeout[o.tId];
2727                             }
2728
2729                             oConn.handleTransactionResponse(o, callback);
2730                         }
2731                     }
2732                     , this.pollInterval);
2733         },
2734
2735         handleTransactionResponse:function(o, callback, isAbort)
2736         {
2737
2738             if (!callback) {
2739                 this.releaseObject(o);
2740                 return;
2741             }
2742
2743             var httpStatus, responseObject;
2744
2745             try
2746             {
2747                 if (o.conn.status !== undefined && o.conn.status != 0) {
2748                     httpStatus = o.conn.status;
2749                 }
2750                 else {
2751                     httpStatus = 13030;
2752                 }
2753             }
2754             catch(e) {
2755
2756
2757                 httpStatus = 13030;
2758             }
2759
2760             if (httpStatus >= 200 && httpStatus < 300) {
2761                 responseObject = this.createResponseObject(o, callback.argument);
2762                 if (callback.success) {
2763                     if (!callback.scope) {
2764                         callback.success(responseObject);
2765                     }
2766                     else {
2767
2768
2769                         callback.success.apply(callback.scope, [responseObject]);
2770                     }
2771                 }
2772             }
2773             else {
2774                 switch (httpStatus) {
2775
2776                     case 12002:
2777                     case 12029:
2778                     case 12030:
2779                     case 12031:
2780                     case 12152:
2781                     case 13030:
2782                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2783                         if (callback.failure) {
2784                             if (!callback.scope) {
2785                                 callback.failure(responseObject);
2786                             }
2787                             else {
2788                                 callback.failure.apply(callback.scope, [responseObject]);
2789                             }
2790                         }
2791                         break;
2792                     default:
2793                         responseObject = this.createResponseObject(o, callback.argument);
2794                         if (callback.failure) {
2795                             if (!callback.scope) {
2796                                 callback.failure(responseObject);
2797                             }
2798                             else {
2799                                 callback.failure.apply(callback.scope, [responseObject]);
2800                             }
2801                         }
2802                 }
2803             }
2804
2805             this.releaseObject(o);
2806             responseObject = null;
2807         },
2808
2809         createResponseObject:function(o, callbackArg)
2810         {
2811             var obj = {};
2812             var headerObj = {};
2813
2814             try
2815             {
2816                 var headerStr = o.conn.getAllResponseHeaders();
2817                 var header = headerStr.split('\n');
2818                 for (var i = 0; i < header.length; i++) {
2819                     var delimitPos = header[i].indexOf(':');
2820                     if (delimitPos != -1) {
2821                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2822                     }
2823                 }
2824             }
2825             catch(e) {
2826             }
2827
2828             obj.tId = o.tId;
2829             obj.status = o.conn.status;
2830             obj.statusText = o.conn.statusText;
2831             obj.getResponseHeader = headerObj;
2832             obj.getAllResponseHeaders = headerStr;
2833             obj.responseText = o.conn.responseText;
2834             obj.responseXML = o.conn.responseXML;
2835
2836             if (typeof callbackArg !== undefined) {
2837                 obj.argument = callbackArg;
2838             }
2839
2840             return obj;
2841         },
2842
2843         createExceptionObject:function(tId, callbackArg, isAbort)
2844         {
2845             var COMM_CODE = 0;
2846             var COMM_ERROR = 'communication failure';
2847             var ABORT_CODE = -1;
2848             var ABORT_ERROR = 'transaction aborted';
2849
2850             var obj = {};
2851
2852             obj.tId = tId;
2853             if (isAbort) {
2854                 obj.status = ABORT_CODE;
2855                 obj.statusText = ABORT_ERROR;
2856             }
2857             else {
2858                 obj.status = COMM_CODE;
2859                 obj.statusText = COMM_ERROR;
2860             }
2861
2862             if (callbackArg) {
2863                 obj.argument = callbackArg;
2864             }
2865
2866             return obj;
2867         },
2868
2869         initHeader:function(label, value, isDefault)
2870         {
2871             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2872
2873             if (headerObj[label] === undefined) {
2874                 headerObj[label] = value;
2875             }
2876             else {
2877
2878
2879                 headerObj[label] = value + "," + headerObj[label];
2880             }
2881
2882             if (isDefault) {
2883                 this.hasDefaultHeaders = true;
2884             }
2885             else {
2886                 this.hasHeaders = true;
2887             }
2888         },
2889
2890
2891         setHeader:function(o)
2892         {
2893             if (this.hasDefaultHeaders) {
2894                 for (var prop in this.defaultHeaders) {
2895                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2896                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2897                     }
2898                 }
2899             }
2900
2901             if (this.hasHeaders) {
2902                 for (var prop in this.headers) {
2903                     if (this.headers.hasOwnProperty(prop)) {
2904                         o.conn.setRequestHeader(prop, this.headers[prop]);
2905                     }
2906                 }
2907                 this.headers = {};
2908                 this.hasHeaders = false;
2909             }
2910         },
2911
2912         resetDefaultHeaders:function() {
2913             delete this.defaultHeaders;
2914             this.defaultHeaders = {};
2915             this.hasDefaultHeaders = false;
2916         },
2917
2918         abort:function(o, callback, isTimeout)
2919         {
2920             if(this.isCallInProgress(o)) {
2921                 o.conn.abort();
2922                 window.clearInterval(this.poll[o.tId]);
2923                 delete this.poll[o.tId];
2924                 if (isTimeout) {
2925                     delete this.timeout[o.tId];
2926                 }
2927
2928                 this.handleTransactionResponse(o, callback, true);
2929
2930                 return true;
2931             }
2932             else {
2933                 return false;
2934             }
2935         },
2936
2937
2938         isCallInProgress:function(o)
2939         {
2940             if (o && o.conn) {
2941                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2942             }
2943             else {
2944
2945                 return false;
2946             }
2947         },
2948
2949
2950         releaseObject:function(o)
2951         {
2952
2953             o.conn = null;
2954
2955             o = null;
2956         },
2957
2958         activeX:[
2959         'MSXML2.XMLHTTP.3.0',
2960         'MSXML2.XMLHTTP',
2961         'Microsoft.XMLHTTP'
2962         ]
2963
2964
2965     };
2966 })();/*
2967  * Portions of this file are based on pieces of Yahoo User Interface Library
2968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2969  * YUI licensed under the BSD License:
2970  * http://developer.yahoo.net/yui/license.txt
2971  * <script type="text/javascript">
2972  *
2973  */
2974
2975 Roo.lib.Region = function(t, r, b, l) {
2976     this.top = t;
2977     this[1] = t;
2978     this.right = r;
2979     this.bottom = b;
2980     this.left = l;
2981     this[0] = l;
2982 };
2983
2984
2985 Roo.lib.Region.prototype = {
2986     contains : function(region) {
2987         return ( region.left >= this.left &&
2988                  region.right <= this.right &&
2989                  region.top >= this.top &&
2990                  region.bottom <= this.bottom    );
2991
2992     },
2993
2994     getArea : function() {
2995         return ( (this.bottom - this.top) * (this.right - this.left) );
2996     },
2997
2998     intersect : function(region) {
2999         var t = Math.max(this.top, region.top);
3000         var r = Math.min(this.right, region.right);
3001         var b = Math.min(this.bottom, region.bottom);
3002         var l = Math.max(this.left, region.left);
3003
3004         if (b >= t && r >= l) {
3005             return new Roo.lib.Region(t, r, b, l);
3006         } else {
3007             return null;
3008         }
3009     },
3010     union : function(region) {
3011         var t = Math.min(this.top, region.top);
3012         var r = Math.max(this.right, region.right);
3013         var b = Math.max(this.bottom, region.bottom);
3014         var l = Math.min(this.left, region.left);
3015
3016         return new Roo.lib.Region(t, r, b, l);
3017     },
3018
3019     adjust : function(t, l, b, r) {
3020         this.top += t;
3021         this.left += l;
3022         this.right += r;
3023         this.bottom += b;
3024         return this;
3025     }
3026 };
3027
3028 Roo.lib.Region.getRegion = function(el) {
3029     var p = Roo.lib.Dom.getXY(el);
3030
3031     var t = p[1];
3032     var r = p[0] + el.offsetWidth;
3033     var b = p[1] + el.offsetHeight;
3034     var l = p[0];
3035
3036     return new Roo.lib.Region(t, r, b, l);
3037 };
3038 /*
3039  * Portions of this file are based on pieces of Yahoo User Interface Library
3040  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3041  * YUI licensed under the BSD License:
3042  * http://developer.yahoo.net/yui/license.txt
3043  * <script type="text/javascript">
3044  *
3045  */
3046 //@@dep Roo.lib.Region
3047
3048
3049 Roo.lib.Point = function(x, y) {
3050     if (x instanceof Array) {
3051         y = x[1];
3052         x = x[0];
3053     }
3054     this.x = this.right = this.left = this[0] = x;
3055     this.y = this.top = this.bottom = this[1] = y;
3056 };
3057
3058 Roo.lib.Point.prototype = new Roo.lib.Region();
3059 /*
3060  * Portions of this file are based on pieces of Yahoo User Interface Library
3061  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3062  * YUI licensed under the BSD License:
3063  * http://developer.yahoo.net/yui/license.txt
3064  * <script type="text/javascript">
3065  *
3066  */
3067  
3068 (function() {   
3069
3070     Roo.lib.Anim = {
3071         scroll : function(el, args, duration, easing, cb, scope) {
3072             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3073         },
3074
3075         motion : function(el, args, duration, easing, cb, scope) {
3076             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3077         },
3078
3079         color : function(el, args, duration, easing, cb, scope) {
3080             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3081         },
3082
3083         run : function(el, args, duration, easing, cb, scope, type) {
3084             type = type || Roo.lib.AnimBase;
3085             if (typeof easing == "string") {
3086                 easing = Roo.lib.Easing[easing];
3087             }
3088             var anim = new type(el, args, duration, easing);
3089             anim.animateX(function() {
3090                 Roo.callback(cb, scope);
3091             });
3092             return anim;
3093         }
3094     };
3095 })();/*
3096  * Portions of this file are based on pieces of Yahoo User Interface Library
3097  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3098  * YUI licensed under the BSD License:
3099  * http://developer.yahoo.net/yui/license.txt
3100  * <script type="text/javascript">
3101  *
3102  */
3103
3104 (function() {    
3105     var libFlyweight;
3106     
3107     function fly(el) {
3108         if (!libFlyweight) {
3109             libFlyweight = new Roo.Element.Flyweight();
3110         }
3111         libFlyweight.dom = el;
3112         return libFlyweight;
3113     }
3114
3115     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3116     
3117    
3118     
3119     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3120         if (el) {
3121             this.init(el, attributes, duration, method);
3122         }
3123     };
3124
3125     Roo.lib.AnimBase.fly = fly;
3126     
3127     
3128     
3129     Roo.lib.AnimBase.prototype = {
3130
3131         toString: function() {
3132             var el = this.getEl();
3133             var id = el.id || el.tagName;
3134             return ("Anim " + id);
3135         },
3136
3137         patterns: {
3138             noNegatives:        /width|height|opacity|padding/i,
3139             offsetAttribute:  /^((width|height)|(top|left))$/,
3140             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3141             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3142         },
3143
3144
3145         doMethod: function(attr, start, end) {
3146             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3147         },
3148
3149
3150         setAttribute: function(attr, val, unit) {
3151             if (this.patterns.noNegatives.test(attr)) {
3152                 val = (val > 0) ? val : 0;
3153             }
3154
3155             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3156         },
3157
3158
3159         getAttribute: function(attr) {
3160             var el = this.getEl();
3161             var val = fly(el).getStyle(attr);
3162
3163             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3164                 return parseFloat(val);
3165             }
3166
3167             var a = this.patterns.offsetAttribute.exec(attr) || [];
3168             var pos = !!( a[3] );
3169             var box = !!( a[2] );
3170
3171
3172             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3173                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3174             } else {
3175                 val = 0;
3176             }
3177
3178             return val;
3179         },
3180
3181
3182         getDefaultUnit: function(attr) {
3183             if (this.patterns.defaultUnit.test(attr)) {
3184                 return 'px';
3185             }
3186
3187             return '';
3188         },
3189
3190         animateX : function(callback, scope) {
3191             var f = function() {
3192                 this.onComplete.removeListener(f);
3193                 if (typeof callback == "function") {
3194                     callback.call(scope || this, this);
3195                 }
3196             };
3197             this.onComplete.addListener(f, this);
3198             this.animate();
3199         },
3200
3201
3202         setRuntimeAttribute: function(attr) {
3203             var start;
3204             var end;
3205             var attributes = this.attributes;
3206
3207             this.runtimeAttributes[attr] = {};
3208
3209             var isset = function(prop) {
3210                 return (typeof prop !== 'undefined');
3211             };
3212
3213             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3214                 return false;
3215             }
3216
3217             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3218
3219
3220             if (isset(attributes[attr]['to'])) {
3221                 end = attributes[attr]['to'];
3222             } else if (isset(attributes[attr]['by'])) {
3223                 if (start.constructor == Array) {
3224                     end = [];
3225                     for (var i = 0, len = start.length; i < len; ++i) {
3226                         end[i] = start[i] + attributes[attr]['by'][i];
3227                     }
3228                 } else {
3229                     end = start + attributes[attr]['by'];
3230                 }
3231             }
3232
3233             this.runtimeAttributes[attr].start = start;
3234             this.runtimeAttributes[attr].end = end;
3235
3236
3237             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3238         },
3239
3240
3241         init: function(el, attributes, duration, method) {
3242
3243             var isAnimated = false;
3244
3245
3246             var startTime = null;
3247
3248
3249             var actualFrames = 0;
3250
3251
3252             el = Roo.getDom(el);
3253
3254
3255             this.attributes = attributes || {};
3256
3257
3258             this.duration = duration || 1;
3259
3260
3261             this.method = method || Roo.lib.Easing.easeNone;
3262
3263
3264             this.useSeconds = true;
3265
3266
3267             this.currentFrame = 0;
3268
3269
3270             this.totalFrames = Roo.lib.AnimMgr.fps;
3271
3272
3273             this.getEl = function() {
3274                 return el;
3275             };
3276
3277
3278             this.isAnimated = function() {
3279                 return isAnimated;
3280             };
3281
3282
3283             this.getStartTime = function() {
3284                 return startTime;
3285             };
3286
3287             this.runtimeAttributes = {};
3288
3289
3290             this.animate = function() {
3291                 if (this.isAnimated()) {
3292                     return false;
3293                 }
3294
3295                 this.currentFrame = 0;
3296
3297                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3298
3299                 Roo.lib.AnimMgr.registerElement(this);
3300             };
3301
3302
3303             this.stop = function(finish) {
3304                 if (finish) {
3305                     this.currentFrame = this.totalFrames;
3306                     this._onTween.fire();
3307                 }
3308                 Roo.lib.AnimMgr.stop(this);
3309             };
3310
3311             var onStart = function() {
3312                 this.onStart.fire();
3313
3314                 this.runtimeAttributes = {};
3315                 for (var attr in this.attributes) {
3316                     this.setRuntimeAttribute(attr);
3317                 }
3318
3319                 isAnimated = true;
3320                 actualFrames = 0;
3321                 startTime = new Date();
3322             };
3323
3324
3325             var onTween = function() {
3326                 var data = {
3327                     duration: new Date() - this.getStartTime(),
3328                     currentFrame: this.currentFrame
3329                 };
3330
3331                 data.toString = function() {
3332                     return (
3333                             'duration: ' + data.duration +
3334                             ', currentFrame: ' + data.currentFrame
3335                             );
3336                 };
3337
3338                 this.onTween.fire(data);
3339
3340                 var runtimeAttributes = this.runtimeAttributes;
3341
3342                 for (var attr in runtimeAttributes) {
3343                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3344                 }
3345
3346                 actualFrames += 1;
3347             };
3348
3349             var onComplete = function() {
3350                 var actual_duration = (new Date() - startTime) / 1000 ;
3351
3352                 var data = {
3353                     duration: actual_duration,
3354                     frames: actualFrames,
3355                     fps: actualFrames / actual_duration
3356                 };
3357
3358                 data.toString = function() {
3359                     return (
3360                             'duration: ' + data.duration +
3361                             ', frames: ' + data.frames +
3362                             ', fps: ' + data.fps
3363                             );
3364                 };
3365
3366                 isAnimated = false;
3367                 actualFrames = 0;
3368                 this.onComplete.fire(data);
3369             };
3370
3371
3372             this._onStart = new Roo.util.Event(this);
3373             this.onStart = new Roo.util.Event(this);
3374             this.onTween = new Roo.util.Event(this);
3375             this._onTween = new Roo.util.Event(this);
3376             this.onComplete = new Roo.util.Event(this);
3377             this._onComplete = new Roo.util.Event(this);
3378             this._onStart.addListener(onStart);
3379             this._onTween.addListener(onTween);
3380             this._onComplete.addListener(onComplete);
3381         }
3382     };
3383 })();
3384 /*
3385  * Portions of this file are based on pieces of Yahoo User Interface Library
3386  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3387  * YUI licensed under the BSD License:
3388  * http://developer.yahoo.net/yui/license.txt
3389  * <script type="text/javascript">
3390  *
3391  */
3392
3393 Roo.lib.AnimMgr = new function() {
3394
3395         var thread = null;
3396
3397
3398         var queue = [];
3399
3400
3401         var tweenCount = 0;
3402
3403
3404         this.fps = 1000;
3405
3406
3407         this.delay = 1;
3408
3409
3410         this.registerElement = function(tween) {
3411             queue[queue.length] = tween;
3412             tweenCount += 1;
3413             tween._onStart.fire();
3414             this.start();
3415         };
3416
3417
3418         this.unRegister = function(tween, index) {
3419             tween._onComplete.fire();
3420             index = index || getIndex(tween);
3421             if (index != -1) {
3422                 queue.splice(index, 1);
3423             }
3424
3425             tweenCount -= 1;
3426             if (tweenCount <= 0) {
3427                 this.stop();
3428             }
3429         };
3430
3431
3432         this.start = function() {
3433             if (thread === null) {
3434                 thread = setInterval(this.run, this.delay);
3435             }
3436         };
3437
3438
3439         this.stop = function(tween) {
3440             if (!tween) {
3441                 clearInterval(thread);
3442
3443                 for (var i = 0, len = queue.length; i < len; ++i) {
3444                     if (queue[0].isAnimated()) {
3445                         this.unRegister(queue[0], 0);
3446                     }
3447                 }
3448
3449                 queue = [];
3450                 thread = null;
3451                 tweenCount = 0;
3452             }
3453             else {
3454                 this.unRegister(tween);
3455             }
3456         };
3457
3458
3459         this.run = function() {
3460             for (var i = 0, len = queue.length; i < len; ++i) {
3461                 var tween = queue[i];
3462                 if (!tween || !tween.isAnimated()) {
3463                     continue;
3464                 }
3465
3466                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3467                 {
3468                     tween.currentFrame += 1;
3469
3470                     if (tween.useSeconds) {
3471                         correctFrame(tween);
3472                     }
3473                     tween._onTween.fire();
3474                 }
3475                 else {
3476                     Roo.lib.AnimMgr.stop(tween, i);
3477                 }
3478             }
3479         };
3480
3481         var getIndex = function(anim) {
3482             for (var i = 0, len = queue.length; i < len; ++i) {
3483                 if (queue[i] == anim) {
3484                     return i;
3485                 }
3486             }
3487             return -1;
3488         };
3489
3490
3491         var correctFrame = function(tween) {
3492             var frames = tween.totalFrames;
3493             var frame = tween.currentFrame;
3494             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3495             var elapsed = (new Date() - tween.getStartTime());
3496             var tweak = 0;
3497
3498             if (elapsed < tween.duration * 1000) {
3499                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3500             } else {
3501                 tweak = frames - (frame + 1);
3502             }
3503             if (tweak > 0 && isFinite(tweak)) {
3504                 if (tween.currentFrame + tweak >= frames) {
3505                     tweak = frames - (frame + 1);
3506                 }
3507
3508                 tween.currentFrame += tweak;
3509             }
3510         };
3511     };/*
3512  * Portions of this file are based on pieces of Yahoo User Interface Library
3513  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3514  * YUI licensed under the BSD License:
3515  * http://developer.yahoo.net/yui/license.txt
3516  * <script type="text/javascript">
3517  *
3518  */
3519 Roo.lib.Bezier = new function() {
3520
3521         this.getPosition = function(points, t) {
3522             var n = points.length;
3523             var tmp = [];
3524
3525             for (var i = 0; i < n; ++i) {
3526                 tmp[i] = [points[i][0], points[i][1]];
3527             }
3528
3529             for (var j = 1; j < n; ++j) {
3530                 for (i = 0; i < n - j; ++i) {
3531                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3532                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3533                 }
3534             }
3535
3536             return [ tmp[0][0], tmp[0][1] ];
3537
3538         };
3539     };/*
3540  * Portions of this file are based on pieces of Yahoo User Interface Library
3541  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3542  * YUI licensed under the BSD License:
3543  * http://developer.yahoo.net/yui/license.txt
3544  * <script type="text/javascript">
3545  *
3546  */
3547 (function() {
3548
3549     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3550         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3551     };
3552
3553     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3554
3555     var fly = Roo.lib.AnimBase.fly;
3556     var Y = Roo.lib;
3557     var superclass = Y.ColorAnim.superclass;
3558     var proto = Y.ColorAnim.prototype;
3559
3560     proto.toString = function() {
3561         var el = this.getEl();
3562         var id = el.id || el.tagName;
3563         return ("ColorAnim " + id);
3564     };
3565
3566     proto.patterns.color = /color$/i;
3567     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3568     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3569     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3570     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3571
3572
3573     proto.parseColor = function(s) {
3574         if (s.length == 3) {
3575             return s;
3576         }
3577
3578         var c = this.patterns.hex.exec(s);
3579         if (c && c.length == 4) {
3580             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3581         }
3582
3583         c = this.patterns.rgb.exec(s);
3584         if (c && c.length == 4) {
3585             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3586         }
3587
3588         c = this.patterns.hex3.exec(s);
3589         if (c && c.length == 4) {
3590             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3591         }
3592
3593         return null;
3594     };
3595     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3596     proto.getAttribute = function(attr) {
3597         var el = this.getEl();
3598         if (this.patterns.color.test(attr)) {
3599             var val = fly(el).getStyle(attr);
3600
3601             if (this.patterns.transparent.test(val)) {
3602                 var parent = el.parentNode;
3603                 val = fly(parent).getStyle(attr);
3604
3605                 while (parent && this.patterns.transparent.test(val)) {
3606                     parent = parent.parentNode;
3607                     val = fly(parent).getStyle(attr);
3608                     if (parent.tagName.toUpperCase() == 'HTML') {
3609                         val = '#fff';
3610                     }
3611                 }
3612             }
3613         } else {
3614             val = superclass.getAttribute.call(this, attr);
3615         }
3616
3617         return val;
3618     };
3619     proto.getAttribute = function(attr) {
3620         var el = this.getEl();
3621         if (this.patterns.color.test(attr)) {
3622             var val = fly(el).getStyle(attr);
3623
3624             if (this.patterns.transparent.test(val)) {
3625                 var parent = el.parentNode;
3626                 val = fly(parent).getStyle(attr);
3627
3628                 while (parent && this.patterns.transparent.test(val)) {
3629                     parent = parent.parentNode;
3630                     val = fly(parent).getStyle(attr);
3631                     if (parent.tagName.toUpperCase() == 'HTML') {
3632                         val = '#fff';
3633                     }
3634                 }
3635             }
3636         } else {
3637             val = superclass.getAttribute.call(this, attr);
3638         }
3639
3640         return val;
3641     };
3642
3643     proto.doMethod = function(attr, start, end) {
3644         var val;
3645
3646         if (this.patterns.color.test(attr)) {
3647             val = [];
3648             for (var i = 0, len = start.length; i < len; ++i) {
3649                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3650             }
3651
3652             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3653         }
3654         else {
3655             val = superclass.doMethod.call(this, attr, start, end);
3656         }
3657
3658         return val;
3659     };
3660
3661     proto.setRuntimeAttribute = function(attr) {
3662         superclass.setRuntimeAttribute.call(this, attr);
3663
3664         if (this.patterns.color.test(attr)) {
3665             var attributes = this.attributes;
3666             var start = this.parseColor(this.runtimeAttributes[attr].start);
3667             var end = this.parseColor(this.runtimeAttributes[attr].end);
3668
3669             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3670                 end = this.parseColor(attributes[attr].by);
3671
3672                 for (var i = 0, len = start.length; i < len; ++i) {
3673                     end[i] = start[i] + end[i];
3674                 }
3675             }
3676
3677             this.runtimeAttributes[attr].start = start;
3678             this.runtimeAttributes[attr].end = end;
3679         }
3680     };
3681 })();
3682
3683 /*
3684  * Portions of this file are based on pieces of Yahoo User Interface Library
3685  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3686  * YUI licensed under the BSD License:
3687  * http://developer.yahoo.net/yui/license.txt
3688  * <script type="text/javascript">
3689  *
3690  */
3691 Roo.lib.Easing = {
3692
3693
3694     easeNone: function (t, b, c, d) {
3695         return c * t / d + b;
3696     },
3697
3698
3699     easeIn: function (t, b, c, d) {
3700         return c * (t /= d) * t + b;
3701     },
3702
3703
3704     easeOut: function (t, b, c, d) {
3705         return -c * (t /= d) * (t - 2) + b;
3706     },
3707
3708
3709     easeBoth: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t + b;
3712         }
3713
3714         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3715     },
3716
3717
3718     easeInStrong: function (t, b, c, d) {
3719         return c * (t /= d) * t * t * t + b;
3720     },
3721
3722
3723     easeOutStrong: function (t, b, c, d) {
3724         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3725     },
3726
3727
3728     easeBothStrong: function (t, b, c, d) {
3729         if ((t /= d / 2) < 1) {
3730             return c / 2 * t * t * t * t + b;
3731         }
3732
3733         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3734     },
3735
3736
3737
3738     elasticIn: function (t, b, c, d, a, p) {
3739         if (t == 0) {
3740             return b;
3741         }
3742         if ((t /= d) == 1) {
3743             return b + c;
3744         }
3745         if (!p) {
3746             p = d * .3;
3747         }
3748
3749         if (!a || a < Math.abs(c)) {
3750             a = c;
3751             var s = p / 4;
3752         }
3753         else {
3754             var s = p / (2 * Math.PI) * Math.asin(c / a);
3755         }
3756
3757         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3758     },
3759
3760
3761     elasticOut: function (t, b, c, d, a, p) {
3762         if (t == 0) {
3763             return b;
3764         }
3765         if ((t /= d) == 1) {
3766             return b + c;
3767         }
3768         if (!p) {
3769             p = d * .3;
3770         }
3771
3772         if (!a || a < Math.abs(c)) {
3773             a = c;
3774             var s = p / 4;
3775         }
3776         else {
3777             var s = p / (2 * Math.PI) * Math.asin(c / a);
3778         }
3779
3780         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3781     },
3782
3783
3784     elasticBoth: function (t, b, c, d, a, p) {
3785         if (t == 0) {
3786             return b;
3787         }
3788
3789         if ((t /= d / 2) == 2) {
3790             return b + c;
3791         }
3792
3793         if (!p) {
3794             p = d * (.3 * 1.5);
3795         }
3796
3797         if (!a || a < Math.abs(c)) {
3798             a = c;
3799             var s = p / 4;
3800         }
3801         else {
3802             var s = p / (2 * Math.PI) * Math.asin(c / a);
3803         }
3804
3805         if (t < 1) {
3806             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3807                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3808         }
3809         return a * Math.pow(2, -10 * (t -= 1)) *
3810                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3811     },
3812
3813
3814
3815     backIn: function (t, b, c, d, s) {
3816         if (typeof s == 'undefined') {
3817             s = 1.70158;
3818         }
3819         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3820     },
3821
3822
3823     backOut: function (t, b, c, d, s) {
3824         if (typeof s == 'undefined') {
3825             s = 1.70158;
3826         }
3827         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3828     },
3829
3830
3831     backBoth: function (t, b, c, d, s) {
3832         if (typeof s == 'undefined') {
3833             s = 1.70158;
3834         }
3835
3836         if ((t /= d / 2 ) < 1) {
3837             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3838         }
3839         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3840     },
3841
3842
3843     bounceIn: function (t, b, c, d) {
3844         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3845     },
3846
3847
3848     bounceOut: function (t, b, c, d) {
3849         if ((t /= d) < (1 / 2.75)) {
3850             return c * (7.5625 * t * t) + b;
3851         } else if (t < (2 / 2.75)) {
3852             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3853         } else if (t < (2.5 / 2.75)) {
3854             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3855         }
3856         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3857     },
3858
3859
3860     bounceBoth: function (t, b, c, d) {
3861         if (t < d / 2) {
3862             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3863         }
3864         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3865     }
3866 };/*
3867  * Portions of this file are based on pieces of Yahoo User Interface Library
3868  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3869  * YUI licensed under the BSD License:
3870  * http://developer.yahoo.net/yui/license.txt
3871  * <script type="text/javascript">
3872  *
3873  */
3874     (function() {
3875         Roo.lib.Motion = function(el, attributes, duration, method) {
3876             if (el) {
3877                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3878             }
3879         };
3880
3881         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3882
3883
3884         var Y = Roo.lib;
3885         var superclass = Y.Motion.superclass;
3886         var proto = Y.Motion.prototype;
3887
3888         proto.toString = function() {
3889             var el = this.getEl();
3890             var id = el.id || el.tagName;
3891             return ("Motion " + id);
3892         };
3893
3894         proto.patterns.points = /^points$/i;
3895
3896         proto.setAttribute = function(attr, val, unit) {
3897             if (this.patterns.points.test(attr)) {
3898                 unit = unit || 'px';
3899                 superclass.setAttribute.call(this, 'left', val[0], unit);
3900                 superclass.setAttribute.call(this, 'top', val[1], unit);
3901             } else {
3902                 superclass.setAttribute.call(this, attr, val, unit);
3903             }
3904         };
3905
3906         proto.getAttribute = function(attr) {
3907             if (this.patterns.points.test(attr)) {
3908                 var val = [
3909                         superclass.getAttribute.call(this, 'left'),
3910                         superclass.getAttribute.call(this, 'top')
3911                         ];
3912             } else {
3913                 val = superclass.getAttribute.call(this, attr);
3914             }
3915
3916             return val;
3917         };
3918
3919         proto.doMethod = function(attr, start, end) {
3920             var val = null;
3921
3922             if (this.patterns.points.test(attr)) {
3923                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3924                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3925             } else {
3926                 val = superclass.doMethod.call(this, attr, start, end);
3927             }
3928             return val;
3929         };
3930
3931         proto.setRuntimeAttribute = function(attr) {
3932             if (this.patterns.points.test(attr)) {
3933                 var el = this.getEl();
3934                 var attributes = this.attributes;
3935                 var start;
3936                 var control = attributes['points']['control'] || [];
3937                 var end;
3938                 var i, len;
3939
3940                 if (control.length > 0 && !(control[0] instanceof Array)) {
3941                     control = [control];
3942                 } else {
3943                     var tmp = [];
3944                     for (i = 0,len = control.length; i < len; ++i) {
3945                         tmp[i] = control[i];
3946                     }
3947                     control = tmp;
3948                 }
3949
3950                 Roo.fly(el).position();
3951
3952                 if (isset(attributes['points']['from'])) {
3953                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3954                 }
3955                 else {
3956                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3957                 }
3958
3959                 start = this.getAttribute('points');
3960
3961
3962                 if (isset(attributes['points']['to'])) {
3963                     end = translateValues.call(this, attributes['points']['to'], start);
3964
3965                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3966                     for (i = 0,len = control.length; i < len; ++i) {
3967                         control[i] = translateValues.call(this, control[i], start);
3968                     }
3969
3970
3971                 } else if (isset(attributes['points']['by'])) {
3972                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3973
3974                     for (i = 0,len = control.length; i < len; ++i) {
3975                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3976                     }
3977                 }
3978
3979                 this.runtimeAttributes[attr] = [start];
3980
3981                 if (control.length > 0) {
3982                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3983                 }
3984
3985                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3986             }
3987             else {
3988                 superclass.setRuntimeAttribute.call(this, attr);
3989             }
3990         };
3991
3992         var translateValues = function(val, start) {
3993             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3994             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3995
3996             return val;
3997         };
3998
3999         var isset = function(prop) {
4000             return (typeof prop !== 'undefined');
4001         };
4002     })();
4003 /*
4004  * Portions of this file are based on pieces of Yahoo User Interface Library
4005  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4006  * YUI licensed under the BSD License:
4007  * http://developer.yahoo.net/yui/license.txt
4008  * <script type="text/javascript">
4009  *
4010  */
4011     (function() {
4012         Roo.lib.Scroll = function(el, attributes, duration, method) {
4013             if (el) {
4014                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4015             }
4016         };
4017
4018         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4019
4020
4021         var Y = Roo.lib;
4022         var superclass = Y.Scroll.superclass;
4023         var proto = Y.Scroll.prototype;
4024
4025         proto.toString = function() {
4026             var el = this.getEl();
4027             var id = el.id || el.tagName;
4028             return ("Scroll " + id);
4029         };
4030
4031         proto.doMethod = function(attr, start, end) {
4032             var val = null;
4033
4034             if (attr == 'scroll') {
4035                 val = [
4036                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4037                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4038                         ];
4039
4040             } else {
4041                 val = superclass.doMethod.call(this, attr, start, end);
4042             }
4043             return val;
4044         };
4045
4046         proto.getAttribute = function(attr) {
4047             var val = null;
4048             var el = this.getEl();
4049
4050             if (attr == 'scroll') {
4051                 val = [ el.scrollLeft, el.scrollTop ];
4052             } else {
4053                 val = superclass.getAttribute.call(this, attr);
4054             }
4055
4056             return val;
4057         };
4058
4059         proto.setAttribute = function(attr, val, unit) {
4060             var el = this.getEl();
4061
4062             if (attr == 'scroll') {
4063                 el.scrollLeft = val[0];
4064                 el.scrollTop = val[1];
4065             } else {
4066                 superclass.setAttribute.call(this, attr, val, unit);
4067             }
4068         };
4069     })();
4070 /*
4071  * Based on:
4072  * Ext JS Library 1.1.1
4073  * Copyright(c) 2006-2007, Ext JS, LLC.
4074  *
4075  * Originally Released Under LGPL - original licence link has changed is not relivant.
4076  *
4077  * Fork - LGPL
4078  * <script type="text/javascript">
4079  */
4080
4081
4082 // nasty IE9 hack - what a pile of crap that is..
4083
4084  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4085     Range.prototype.createContextualFragment = function (html) {
4086         var doc = window.document;
4087         var container = doc.createElement("div");
4088         container.innerHTML = html;
4089         var frag = doc.createDocumentFragment(), n;
4090         while ((n = container.firstChild)) {
4091             frag.appendChild(n);
4092         }
4093         return frag;
4094     };
4095 }
4096
4097 /**
4098  * @class Roo.DomHelper
4099  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4100  * 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>.
4101  * @singleton
4102  */
4103 Roo.DomHelper = function(){
4104     var tempTableEl = null;
4105     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4106     var tableRe = /^table|tbody|tr|td$/i;
4107     var xmlns = {};
4108     // build as innerHTML where available
4109     /** @ignore */
4110     var createHtml = function(o){
4111         if(typeof o == 'string'){
4112             return o;
4113         }
4114         var b = "";
4115         if(!o.tag){
4116             o.tag = "div";
4117         }
4118         b += "<" + o.tag;
4119         for(var attr in o){
4120             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4121             if(attr == "style"){
4122                 var s = o["style"];
4123                 if(typeof s == "function"){
4124                     s = s.call();
4125                 }
4126                 if(typeof s == "string"){
4127                     b += ' style="' + s + '"';
4128                 }else if(typeof s == "object"){
4129                     b += ' style="';
4130                     for(var key in s){
4131                         if(typeof s[key] != "function"){
4132                             b += key + ":" + s[key] + ";";
4133                         }
4134                     }
4135                     b += '"';
4136                 }
4137             }else{
4138                 if(attr == "cls"){
4139                     b += ' class="' + o["cls"] + '"';
4140                 }else if(attr == "htmlFor"){
4141                     b += ' for="' + o["htmlFor"] + '"';
4142                 }else{
4143                     b += " " + attr + '="' + o[attr] + '"';
4144                 }
4145             }
4146         }
4147         if(emptyTags.test(o.tag)){
4148             b += "/>";
4149         }else{
4150             b += ">";
4151             var cn = o.children || o.cn;
4152             if(cn){
4153                 //http://bugs.kde.org/show_bug.cgi?id=71506
4154                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4155                     for(var i = 0, len = cn.length; i < len; i++) {
4156                         b += createHtml(cn[i], b);
4157                     }
4158                 }else{
4159                     b += createHtml(cn, b);
4160                 }
4161             }
4162             if(o.html){
4163                 b += o.html;
4164             }
4165             b += "</" + o.tag + ">";
4166         }
4167         return b;
4168     };
4169
4170     // build as dom
4171     /** @ignore */
4172     var createDom = function(o, parentNode){
4173          
4174         // defininition craeted..
4175         var ns = false;
4176         if (o.ns && o.ns != 'html') {
4177                
4178             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4179                 xmlns[o.ns] = o.xmlns;
4180                 ns = o.xmlns;
4181             }
4182             if (typeof(xmlns[o.ns]) == 'undefined') {
4183                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4184             }
4185             ns = xmlns[o.ns];
4186         }
4187         
4188         
4189         if (typeof(o) == 'string') {
4190             return parentNode.appendChild(document.createTextNode(o));
4191         }
4192         o.tag = o.tag || div;
4193         if (o.ns && Roo.isIE) {
4194             ns = false;
4195             o.tag = o.ns + ':' + o.tag;
4196             
4197         }
4198         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4199         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4200         for(var attr in o){
4201             
4202             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4203                     attr == "style" || typeof o[attr] == "function") continue;
4204                     
4205             if(attr=="cls" && Roo.isIE){
4206                 el.className = o["cls"];
4207             }else{
4208                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4209                 else el[attr] = o[attr];
4210             }
4211         }
4212         Roo.DomHelper.applyStyles(el, o.style);
4213         var cn = o.children || o.cn;
4214         if(cn){
4215             //http://bugs.kde.org/show_bug.cgi?id=71506
4216              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4217                 for(var i = 0, len = cn.length; i < len; i++) {
4218                     createDom(cn[i], el);
4219                 }
4220             }else{
4221                 createDom(cn, el);
4222             }
4223         }
4224         if(o.html){
4225             el.innerHTML = o.html;
4226         }
4227         if(parentNode){
4228            parentNode.appendChild(el);
4229         }
4230         return el;
4231     };
4232
4233     var ieTable = function(depth, s, h, e){
4234         tempTableEl.innerHTML = [s, h, e].join('');
4235         var i = -1, el = tempTableEl;
4236         while(++i < depth){
4237             el = el.firstChild;
4238         }
4239         return el;
4240     };
4241
4242     // kill repeat to save bytes
4243     var ts = '<table>',
4244         te = '</table>',
4245         tbs = ts+'<tbody>',
4246         tbe = '</tbody>'+te,
4247         trs = tbs + '<tr>',
4248         tre = '</tr>'+tbe;
4249
4250     /**
4251      * @ignore
4252      * Nasty code for IE's broken table implementation
4253      */
4254     var insertIntoTable = function(tag, where, el, html){
4255         if(!tempTableEl){
4256             tempTableEl = document.createElement('div');
4257         }
4258         var node;
4259         var before = null;
4260         if(tag == 'td'){
4261             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4262                 return;
4263             }
4264             if(where == 'beforebegin'){
4265                 before = el;
4266                 el = el.parentNode;
4267             } else{
4268                 before = el.nextSibling;
4269                 el = el.parentNode;
4270             }
4271             node = ieTable(4, trs, html, tre);
4272         }
4273         else if(tag == 'tr'){
4274             if(where == 'beforebegin'){
4275                 before = el;
4276                 el = el.parentNode;
4277                 node = ieTable(3, tbs, html, tbe);
4278             } else if(where == 'afterend'){
4279                 before = el.nextSibling;
4280                 el = el.parentNode;
4281                 node = ieTable(3, tbs, html, tbe);
4282             } else{ // INTO a TR
4283                 if(where == 'afterbegin'){
4284                     before = el.firstChild;
4285                 }
4286                 node = ieTable(4, trs, html, tre);
4287             }
4288         } else if(tag == 'tbody'){
4289             if(where == 'beforebegin'){
4290                 before = el;
4291                 el = el.parentNode;
4292                 node = ieTable(2, ts, html, te);
4293             } else if(where == 'afterend'){
4294                 before = el.nextSibling;
4295                 el = el.parentNode;
4296                 node = ieTable(2, ts, html, te);
4297             } else{
4298                 if(where == 'afterbegin'){
4299                     before = el.firstChild;
4300                 }
4301                 node = ieTable(3, tbs, html, tbe);
4302             }
4303         } else{ // TABLE
4304             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4305                 return;
4306             }
4307             if(where == 'afterbegin'){
4308                 before = el.firstChild;
4309             }
4310             node = ieTable(2, ts, html, te);
4311         }
4312         el.insertBefore(node, before);
4313         return node;
4314     };
4315
4316     return {
4317     /** True to force the use of DOM instead of html fragments @type Boolean */
4318     useDom : false,
4319
4320     /**
4321      * Returns the markup for the passed Element(s) config
4322      * @param {Object} o The Dom object spec (and children)
4323      * @return {String}
4324      */
4325     markup : function(o){
4326         return createHtml(o);
4327     },
4328
4329     /**
4330      * Applies a style specification to an element
4331      * @param {String/HTMLElement} el The element to apply styles to
4332      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4333      * a function which returns such a specification.
4334      */
4335     applyStyles : function(el, styles){
4336         if(styles){
4337            el = Roo.fly(el);
4338            if(typeof styles == "string"){
4339                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4340                var matches;
4341                while ((matches = re.exec(styles)) != null){
4342                    el.setStyle(matches[1], matches[2]);
4343                }
4344            }else if (typeof styles == "object"){
4345                for (var style in styles){
4346                   el.setStyle(style, styles[style]);
4347                }
4348            }else if (typeof styles == "function"){
4349                 Roo.DomHelper.applyStyles(el, styles.call());
4350            }
4351         }
4352     },
4353
4354     /**
4355      * Inserts an HTML fragment into the Dom
4356      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4357      * @param {HTMLElement} el The context element
4358      * @param {String} html The HTML fragmenet
4359      * @return {HTMLElement} The new node
4360      */
4361     insertHtml : function(where, el, html){
4362         where = where.toLowerCase();
4363         if(el.insertAdjacentHTML){
4364             if(tableRe.test(el.tagName)){
4365                 var rs;
4366                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4367                     return rs;
4368                 }
4369             }
4370             switch(where){
4371                 case "beforebegin":
4372                     el.insertAdjacentHTML('BeforeBegin', html);
4373                     return el.previousSibling;
4374                 case "afterbegin":
4375                     el.insertAdjacentHTML('AfterBegin', html);
4376                     return el.firstChild;
4377                 case "beforeend":
4378                     el.insertAdjacentHTML('BeforeEnd', html);
4379                     return el.lastChild;
4380                 case "afterend":
4381                     el.insertAdjacentHTML('AfterEnd', html);
4382                     return el.nextSibling;
4383             }
4384             throw 'Illegal insertion point -> "' + where + '"';
4385         }
4386         var range = el.ownerDocument.createRange();
4387         var frag;
4388         switch(where){
4389              case "beforebegin":
4390                 range.setStartBefore(el);
4391                 frag = range.createContextualFragment(html);
4392                 el.parentNode.insertBefore(frag, el);
4393                 return el.previousSibling;
4394              case "afterbegin":
4395                 if(el.firstChild){
4396                     range.setStartBefore(el.firstChild);
4397                     frag = range.createContextualFragment(html);
4398                     el.insertBefore(frag, el.firstChild);
4399                     return el.firstChild;
4400                 }else{
4401                     el.innerHTML = html;
4402                     return el.firstChild;
4403                 }
4404             case "beforeend":
4405                 if(el.lastChild){
4406                     range.setStartAfter(el.lastChild);
4407                     frag = range.createContextualFragment(html);
4408                     el.appendChild(frag);
4409                     return el.lastChild;
4410                 }else{
4411                     el.innerHTML = html;
4412                     return el.lastChild;
4413                 }
4414             case "afterend":
4415                 range.setStartAfter(el);
4416                 frag = range.createContextualFragment(html);
4417                 el.parentNode.insertBefore(frag, el.nextSibling);
4418                 return el.nextSibling;
4419             }
4420             throw 'Illegal insertion point -> "' + where + '"';
4421     },
4422
4423     /**
4424      * Creates new Dom element(s) and inserts them before el
4425      * @param {String/HTMLElement/Element} el The context element
4426      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4427      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4428      * @return {HTMLElement/Roo.Element} The new node
4429      */
4430     insertBefore : function(el, o, returnElement){
4431         return this.doInsert(el, o, returnElement, "beforeBegin");
4432     },
4433
4434     /**
4435      * Creates new Dom element(s) and inserts them after el
4436      * @param {String/HTMLElement/Element} el The context element
4437      * @param {Object} o The Dom object spec (and children)
4438      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4439      * @return {HTMLElement/Roo.Element} The new node
4440      */
4441     insertAfter : function(el, o, returnElement){
4442         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and inserts them as the first child of el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     insertFirst : function(el, o, returnElement){
4453         return this.doInsert(el, o, returnElement, "afterBegin");
4454     },
4455
4456     // private
4457     doInsert : function(el, o, returnElement, pos, sibling){
4458         el = Roo.getDom(el);
4459         var newNode;
4460         if(this.useDom || o.ns){
4461             newNode = createDom(o, null);
4462             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4463         }else{
4464             var html = createHtml(o);
4465             newNode = this.insertHtml(pos, el, html);
4466         }
4467         return returnElement ? Roo.get(newNode, true) : newNode;
4468     },
4469
4470     /**
4471      * Creates new Dom element(s) and appends them to el
4472      * @param {String/HTMLElement/Element} el The context element
4473      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4474      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4475      * @return {HTMLElement/Roo.Element} The new node
4476      */
4477     append : function(el, o, returnElement){
4478         el = Roo.getDom(el);
4479         var newNode;
4480         if(this.useDom || o.ns){
4481             newNode = createDom(o, null);
4482             el.appendChild(newNode);
4483         }else{
4484             var html = createHtml(o);
4485             newNode = this.insertHtml("beforeEnd", el, html);
4486         }
4487         return returnElement ? Roo.get(newNode, true) : newNode;
4488     },
4489
4490     /**
4491      * Creates new Dom element(s) and overwrites the contents of el with them
4492      * @param {String/HTMLElement/Element} el The context element
4493      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4494      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4495      * @return {HTMLElement/Roo.Element} The new node
4496      */
4497     overwrite : function(el, o, returnElement){
4498         el = Roo.getDom(el);
4499         if (o.ns) {
4500           
4501             while (el.childNodes.length) {
4502                 el.removeChild(el.firstChild);
4503             }
4504             createDom(o, el);
4505         } else {
4506             el.innerHTML = createHtml(o);   
4507         }
4508         
4509         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4510     },
4511
4512     /**
4513      * Creates a new Roo.DomHelper.Template from the Dom object spec
4514      * @param {Object} o The Dom object spec (and children)
4515      * @return {Roo.DomHelper.Template} The new template
4516      */
4517     createTemplate : function(o){
4518         var html = createHtml(o);
4519         return new Roo.Template(html);
4520     }
4521     };
4522 }();
4523 /*
4524  * Based on:
4525  * Ext JS Library 1.1.1
4526  * Copyright(c) 2006-2007, Ext JS, LLC.
4527  *
4528  * Originally Released Under LGPL - original licence link has changed is not relivant.
4529  *
4530  * Fork - LGPL
4531  * <script type="text/javascript">
4532  */
4533  
4534 /**
4535 * @class Roo.Template
4536 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4537 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4538 * Usage:
4539 <pre><code>
4540 var t = new Roo.Template({
4541     html :  '&lt;div name="{id}"&gt;' + 
4542         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4543         '&lt;/div&gt;',
4544     myformat: function (value, allValues) {
4545         return 'XX' + value;
4546     }
4547 });
4548 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4549 </code></pre>
4550 * For more information see this blog post with examples:
4551 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4552      - Create Elements using DOM, HTML fragments and Templates</a>. 
4553 * @constructor
4554 * @param {Object} cfg - Configuration object.
4555 */
4556 Roo.Template = function(cfg){
4557     // BC!
4558     if(cfg instanceof Array){
4559         cfg = cfg.join("");
4560     }else if(arguments.length > 1){
4561         cfg = Array.prototype.join.call(arguments, "");
4562     }
4563     
4564     
4565     if (typeof(cfg) == 'object') {
4566         Roo.apply(this,cfg)
4567     } else {
4568         // bc
4569         this.html = cfg;
4570     }
4571     if (this.url) {
4572         this.load();
4573     }
4574     
4575 };
4576 Roo.Template.prototype = {
4577     
4578     /**
4579      * @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..
4580      *                    it should be fixed so that template is observable...
4581      */
4582     url : false,
4583     /**
4584      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4585      */
4586     html : '',
4587     /**
4588      * Returns an HTML fragment of this template with the specified values applied.
4589      * @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'})
4590      * @return {String} The HTML fragment
4591      */
4592     applyTemplate : function(values){
4593         try {
4594            
4595             if(this.compiled){
4596                 return this.compiled(values);
4597             }
4598             var useF = this.disableFormats !== true;
4599             var fm = Roo.util.Format, tpl = this;
4600             var fn = function(m, name, format, args){
4601                 if(format && useF){
4602                     if(format.substr(0, 5) == "this."){
4603                         return tpl.call(format.substr(5), values[name], values);
4604                     }else{
4605                         if(args){
4606                             // quoted values are required for strings in compiled templates, 
4607                             // but for non compiled we need to strip them
4608                             // quoted reversed for jsmin
4609                             var re = /^\s*['"](.*)["']\s*$/;
4610                             args = args.split(',');
4611                             for(var i = 0, len = args.length; i < len; i++){
4612                                 args[i] = args[i].replace(re, "$1");
4613                             }
4614                             args = [values[name]].concat(args);
4615                         }else{
4616                             args = [values[name]];
4617                         }
4618                         return fm[format].apply(fm, args);
4619                     }
4620                 }else{
4621                     return values[name] !== undefined ? values[name] : "";
4622                 }
4623             };
4624             return this.html.replace(this.re, fn);
4625         } catch (e) {
4626             Roo.log(e);
4627             throw e;
4628         }
4629          
4630     },
4631     
4632     loading : false,
4633       
4634     load : function ()
4635     {
4636          
4637         if (this.loading) {
4638             return;
4639         }
4640         var _t = this;
4641         
4642         this.loading = true;
4643         this.compiled = false;
4644         
4645         var cx = new Roo.data.Connection();
4646         cx.request({
4647             url : this.url,
4648             method : 'GET',
4649             success : function (response) {
4650                 _t.loading = false;
4651                 _t.html = response.responseText;
4652                 _t.url = false;
4653                 _t.compile();
4654              },
4655             failure : function(response) {
4656                 Roo.log("Template failed to load from " + _t.url);
4657                 _t.loading = false;
4658             }
4659         });
4660     },
4661
4662     /**
4663      * Sets the HTML used as the template and optionally compiles it.
4664      * @param {String} html
4665      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4666      * @return {Roo.Template} this
4667      */
4668     set : function(html, compile){
4669         this.html = html;
4670         this.compiled = null;
4671         if(compile){
4672             this.compile();
4673         }
4674         return this;
4675     },
4676     
4677     /**
4678      * True to disable format functions (defaults to false)
4679      * @type Boolean
4680      */
4681     disableFormats : false,
4682     
4683     /**
4684     * The regular expression used to match template variables 
4685     * @type RegExp
4686     * @property 
4687     */
4688     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4689     
4690     /**
4691      * Compiles the template into an internal function, eliminating the RegEx overhead.
4692      * @return {Roo.Template} this
4693      */
4694     compile : function(){
4695         var fm = Roo.util.Format;
4696         var useF = this.disableFormats !== true;
4697         var sep = Roo.isGecko ? "+" : ",";
4698         var fn = function(m, name, format, args){
4699             if(format && useF){
4700                 args = args ? ',' + args : "";
4701                 if(format.substr(0, 5) != "this."){
4702                     format = "fm." + format + '(';
4703                 }else{
4704                     format = 'this.call("'+ format.substr(5) + '", ';
4705                     args = ", values";
4706                 }
4707             }else{
4708                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4709             }
4710             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4711         };
4712         var body;
4713         // branched to use + in gecko and [].join() in others
4714         if(Roo.isGecko){
4715             body = "this.compiled = function(values){ return '" +
4716                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4717                     "';};";
4718         }else{
4719             body = ["this.compiled = function(values){ return ['"];
4720             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4721             body.push("'].join('');};");
4722             body = body.join('');
4723         }
4724         /**
4725          * eval:var:values
4726          * eval:var:fm
4727          */
4728         eval(body);
4729         return this;
4730     },
4731     
4732     // private function used to call members
4733     call : function(fnName, value, allValues){
4734         return this[fnName](value, allValues);
4735     },
4736     
4737     /**
4738      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4739      * @param {String/HTMLElement/Roo.Element} el The context element
4740      * @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'})
4741      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4742      * @return {HTMLElement/Roo.Element} The new node or Element
4743      */
4744     insertFirst: function(el, values, returnElement){
4745         return this.doInsert('afterBegin', el, values, returnElement);
4746     },
4747
4748     /**
4749      * Applies the supplied values to the template and inserts the new node(s) before el.
4750      * @param {String/HTMLElement/Roo.Element} el The context element
4751      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4752      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4753      * @return {HTMLElement/Roo.Element} The new node or Element
4754      */
4755     insertBefore: function(el, values, returnElement){
4756         return this.doInsert('beforeBegin', el, values, returnElement);
4757     },
4758
4759     /**
4760      * Applies the supplied values to the template and inserts the new node(s) after el.
4761      * @param {String/HTMLElement/Roo.Element} el The context element
4762      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4763      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4764      * @return {HTMLElement/Roo.Element} The new node or Element
4765      */
4766     insertAfter : function(el, values, returnElement){
4767         return this.doInsert('afterEnd', el, values, returnElement);
4768     },
4769     
4770     /**
4771      * Applies the supplied values to the template and appends the new node(s) to el.
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     append : function(el, values, returnElement){
4778         return this.doInsert('beforeEnd', el, values, returnElement);
4779     },
4780
4781     doInsert : function(where, el, values, returnEl){
4782         el = Roo.getDom(el);
4783         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4784         return returnEl ? Roo.get(newNode, true) : newNode;
4785     },
4786
4787     /**
4788      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4789      * @param {String/HTMLElement/Roo.Element} el The context element
4790      * @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'})
4791      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4792      * @return {HTMLElement/Roo.Element} The new node or Element
4793      */
4794     overwrite : function(el, values, returnElement){
4795         el = Roo.getDom(el);
4796         el.innerHTML = this.applyTemplate(values);
4797         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4798     }
4799 };
4800 /**
4801  * Alias for {@link #applyTemplate}
4802  * @method
4803  */
4804 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4805
4806 // backwards compat
4807 Roo.DomHelper.Template = Roo.Template;
4808
4809 /**
4810  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4811  * @param {String/HTMLElement} el A DOM element or its id
4812  * @returns {Roo.Template} The created template
4813  * @static
4814  */
4815 Roo.Template.from = function(el){
4816     el = Roo.getDom(el);
4817     return new Roo.Template(el.value || el.innerHTML);
4818 };/*
4819  * Based on:
4820  * Ext JS Library 1.1.1
4821  * Copyright(c) 2006-2007, Ext JS, LLC.
4822  *
4823  * Originally Released Under LGPL - original licence link has changed is not relivant.
4824  *
4825  * Fork - LGPL
4826  * <script type="text/javascript">
4827  */
4828  
4829
4830 /*
4831  * This is code is also distributed under MIT license for use
4832  * with jQuery and prototype JavaScript libraries.
4833  */
4834 /**
4835  * @class Roo.DomQuery
4836 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).
4837 <p>
4838 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>
4839
4840 <p>
4841 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.
4842 </p>
4843 <h4>Element Selectors:</h4>
4844 <ul class="list">
4845     <li> <b>*</b> any element</li>
4846     <li> <b>E</b> an element with the tag E</li>
4847     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4848     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4849     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4850     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4851 </ul>
4852 <h4>Attribute Selectors:</h4>
4853 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4854 <ul class="list">
4855     <li> <b>E[foo]</b> has an attribute "foo"</li>
4856     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4857     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4858     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4859     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4860     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4861     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4862 </ul>
4863 <h4>Pseudo Classes:</h4>
4864 <ul class="list">
4865     <li> <b>E:first-child</b> E is the first child of its parent</li>
4866     <li> <b>E:last-child</b> E is the last child of its parent</li>
4867     <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>
4868     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4869     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4870     <li> <b>E:only-child</b> E is the only child of its parent</li>
4871     <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>
4872     <li> <b>E:first</b> the first E in the resultset</li>
4873     <li> <b>E:last</b> the last E in the resultset</li>
4874     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4875     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4876     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4877     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4878     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4879     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4880     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4881     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4882     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4883 </ul>
4884 <h4>CSS Value Selectors:</h4>
4885 <ul class="list">
4886     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4887     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4888     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4889     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4890     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4891     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4892 </ul>
4893  * @singleton
4894  */
4895 Roo.DomQuery = function(){
4896     var cache = {}, simpleCache = {}, valueCache = {};
4897     var nonSpace = /\S/;
4898     var trimRe = /^\s+|\s+$/g;
4899     var tplRe = /\{(\d+)\}/g;
4900     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4901     var tagTokenRe = /^(#)?([\w-\*]+)/;
4902     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4903
4904     function child(p, index){
4905         var i = 0;
4906         var n = p.firstChild;
4907         while(n){
4908             if(n.nodeType == 1){
4909                if(++i == index){
4910                    return n;
4911                }
4912             }
4913             n = n.nextSibling;
4914         }
4915         return null;
4916     };
4917
4918     function next(n){
4919         while((n = n.nextSibling) && n.nodeType != 1);
4920         return n;
4921     };
4922
4923     function prev(n){
4924         while((n = n.previousSibling) && n.nodeType != 1);
4925         return n;
4926     };
4927
4928     function children(d){
4929         var n = d.firstChild, ni = -1;
4930             while(n){
4931                 var nx = n.nextSibling;
4932                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4933                     d.removeChild(n);
4934                 }else{
4935                     n.nodeIndex = ++ni;
4936                 }
4937                 n = nx;
4938             }
4939             return this;
4940         };
4941
4942     function byClassName(c, a, v){
4943         if(!v){
4944             return c;
4945         }
4946         var r = [], ri = -1, cn;
4947         for(var i = 0, ci; ci = c[i]; i++){
4948             if((' '+ci.className+' ').indexOf(v) != -1){
4949                 r[++ri] = ci;
4950             }
4951         }
4952         return r;
4953     };
4954
4955     function attrValue(n, attr){
4956         if(!n.tagName && typeof n.length != "undefined"){
4957             n = n[0];
4958         }
4959         if(!n){
4960             return null;
4961         }
4962         if(attr == "for"){
4963             return n.htmlFor;
4964         }
4965         if(attr == "class" || attr == "className"){
4966             return n.className;
4967         }
4968         return n.getAttribute(attr) || n[attr];
4969
4970     };
4971
4972     function getNodes(ns, mode, tagName){
4973         var result = [], ri = -1, cs;
4974         if(!ns){
4975             return result;
4976         }
4977         tagName = tagName || "*";
4978         if(typeof ns.getElementsByTagName != "undefined"){
4979             ns = [ns];
4980         }
4981         if(!mode){
4982             for(var i = 0, ni; ni = ns[i]; i++){
4983                 cs = ni.getElementsByTagName(tagName);
4984                 for(var j = 0, ci; ci = cs[j]; j++){
4985                     result[++ri] = ci;
4986                 }
4987             }
4988         }else if(mode == "/" || mode == ">"){
4989             var utag = tagName.toUpperCase();
4990             for(var i = 0, ni, cn; ni = ns[i]; i++){
4991                 cn = ni.children || ni.childNodes;
4992                 for(var j = 0, cj; cj = cn[j]; j++){
4993                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4994                         result[++ri] = cj;
4995                     }
4996                 }
4997             }
4998         }else if(mode == "+"){
4999             var utag = tagName.toUpperCase();
5000             for(var i = 0, n; n = ns[i]; i++){
5001                 while((n = n.nextSibling) && n.nodeType != 1);
5002                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5003                     result[++ri] = n;
5004                 }
5005             }
5006         }else if(mode == "~"){
5007             for(var i = 0, n; n = ns[i]; i++){
5008                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5009                 if(n){
5010                     result[++ri] = n;
5011                 }
5012             }
5013         }
5014         return result;
5015     };
5016
5017     function concat(a, b){
5018         if(b.slice){
5019             return a.concat(b);
5020         }
5021         for(var i = 0, l = b.length; i < l; i++){
5022             a[a.length] = b[i];
5023         }
5024         return a;
5025     }
5026
5027     function byTag(cs, tagName){
5028         if(cs.tagName || cs == document){
5029             cs = [cs];
5030         }
5031         if(!tagName){
5032             return cs;
5033         }
5034         var r = [], ri = -1;
5035         tagName = tagName.toLowerCase();
5036         for(var i = 0, ci; ci = cs[i]; i++){
5037             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5038                 r[++ri] = ci;
5039             }
5040         }
5041         return r;
5042     };
5043
5044     function byId(cs, attr, id){
5045         if(cs.tagName || cs == document){
5046             cs = [cs];
5047         }
5048         if(!id){
5049             return cs;
5050         }
5051         var r = [], ri = -1;
5052         for(var i = 0,ci; ci = cs[i]; i++){
5053             if(ci && ci.id == id){
5054                 r[++ri] = ci;
5055                 return r;
5056             }
5057         }
5058         return r;
5059     };
5060
5061     function byAttribute(cs, attr, value, op, custom){
5062         var r = [], ri = -1, st = custom=="{";
5063         var f = Roo.DomQuery.operators[op];
5064         for(var i = 0, ci; ci = cs[i]; i++){
5065             var a;
5066             if(st){
5067                 a = Roo.DomQuery.getStyle(ci, attr);
5068             }
5069             else if(attr == "class" || attr == "className"){
5070                 a = ci.className;
5071             }else if(attr == "for"){
5072                 a = ci.htmlFor;
5073             }else if(attr == "href"){
5074                 a = ci.getAttribute("href", 2);
5075             }else{
5076                 a = ci.getAttribute(attr);
5077             }
5078             if((f && f(a, value)) || (!f && a)){
5079                 r[++ri] = ci;
5080             }
5081         }
5082         return r;
5083     };
5084
5085     function byPseudo(cs, name, value){
5086         return Roo.DomQuery.pseudos[name](cs, value);
5087     };
5088
5089     // This is for IE MSXML which does not support expandos.
5090     // IE runs the same speed using setAttribute, however FF slows way down
5091     // and Safari completely fails so they need to continue to use expandos.
5092     var isIE = window.ActiveXObject ? true : false;
5093
5094     // this eval is stop the compressor from
5095     // renaming the variable to something shorter
5096     
5097     /** eval:var:batch */
5098     var batch = 30803; 
5099
5100     var key = 30803;
5101
5102     function nodupIEXml(cs){
5103         var d = ++key;
5104         cs[0].setAttribute("_nodup", d);
5105         var r = [cs[0]];
5106         for(var i = 1, len = cs.length; i < len; i++){
5107             var c = cs[i];
5108             if(!c.getAttribute("_nodup") != d){
5109                 c.setAttribute("_nodup", d);
5110                 r[r.length] = c;
5111             }
5112         }
5113         for(var i = 0, len = cs.length; i < len; i++){
5114             cs[i].removeAttribute("_nodup");
5115         }
5116         return r;
5117     }
5118
5119     function nodup(cs){
5120         if(!cs){
5121             return [];
5122         }
5123         var len = cs.length, c, i, r = cs, cj, ri = -1;
5124         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5125             return cs;
5126         }
5127         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5128             return nodupIEXml(cs);
5129         }
5130         var d = ++key;
5131         cs[0]._nodup = d;
5132         for(i = 1; c = cs[i]; i++){
5133             if(c._nodup != d){
5134                 c._nodup = d;
5135             }else{
5136                 r = [];
5137                 for(var j = 0; j < i; j++){
5138                     r[++ri] = cs[j];
5139                 }
5140                 for(j = i+1; cj = cs[j]; j++){
5141                     if(cj._nodup != d){
5142                         cj._nodup = d;
5143                         r[++ri] = cj;
5144                     }
5145                 }
5146                 return r;
5147             }
5148         }
5149         return r;
5150     }
5151
5152     function quickDiffIEXml(c1, c2){
5153         var d = ++key;
5154         for(var i = 0, len = c1.length; i < len; i++){
5155             c1[i].setAttribute("_qdiff", d);
5156         }
5157         var r = [];
5158         for(var i = 0, len = c2.length; i < len; i++){
5159             if(c2[i].getAttribute("_qdiff") != d){
5160                 r[r.length] = c2[i];
5161             }
5162         }
5163         for(var i = 0, len = c1.length; i < len; i++){
5164            c1[i].removeAttribute("_qdiff");
5165         }
5166         return r;
5167     }
5168
5169     function quickDiff(c1, c2){
5170         var len1 = c1.length;
5171         if(!len1){
5172             return c2;
5173         }
5174         if(isIE && c1[0].selectSingleNode){
5175             return quickDiffIEXml(c1, c2);
5176         }
5177         var d = ++key;
5178         for(var i = 0; i < len1; i++){
5179             c1[i]._qdiff = d;
5180         }
5181         var r = [];
5182         for(var i = 0, len = c2.length; i < len; i++){
5183             if(c2[i]._qdiff != d){
5184                 r[r.length] = c2[i];
5185             }
5186         }
5187         return r;
5188     }
5189
5190     function quickId(ns, mode, root, id){
5191         if(ns == root){
5192            var d = root.ownerDocument || root;
5193            return d.getElementById(id);
5194         }
5195         ns = getNodes(ns, mode, "*");
5196         return byId(ns, null, id);
5197     }
5198
5199     return {
5200         getStyle : function(el, name){
5201             return Roo.fly(el).getStyle(name);
5202         },
5203         /**
5204          * Compiles a selector/xpath query into a reusable function. The returned function
5205          * takes one parameter "root" (optional), which is the context node from where the query should start.
5206          * @param {String} selector The selector/xpath query
5207          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5208          * @return {Function}
5209          */
5210         compile : function(path, type){
5211             type = type || "select";
5212             
5213             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5214             var q = path, mode, lq;
5215             var tk = Roo.DomQuery.matchers;
5216             var tklen = tk.length;
5217             var mm;
5218
5219             // accept leading mode switch
5220             var lmode = q.match(modeRe);
5221             if(lmode && lmode[1]){
5222                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5223                 q = q.replace(lmode[1], "");
5224             }
5225             // strip leading slashes
5226             while(path.substr(0, 1)=="/"){
5227                 path = path.substr(1);
5228             }
5229
5230             while(q && lq != q){
5231                 lq = q;
5232                 var tm = q.match(tagTokenRe);
5233                 if(type == "select"){
5234                     if(tm){
5235                         if(tm[1] == "#"){
5236                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5237                         }else{
5238                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5239                         }
5240                         q = q.replace(tm[0], "");
5241                     }else if(q.substr(0, 1) != '@'){
5242                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5243                     }
5244                 }else{
5245                     if(tm){
5246                         if(tm[1] == "#"){
5247                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5248                         }else{
5249                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5250                         }
5251                         q = q.replace(tm[0], "");
5252                     }
5253                 }
5254                 while(!(mm = q.match(modeRe))){
5255                     var matched = false;
5256                     for(var j = 0; j < tklen; j++){
5257                         var t = tk[j];
5258                         var m = q.match(t.re);
5259                         if(m){
5260                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5261                                                     return m[i];
5262                                                 });
5263                             q = q.replace(m[0], "");
5264                             matched = true;
5265                             break;
5266                         }
5267                     }
5268                     // prevent infinite loop on bad selector
5269                     if(!matched){
5270                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5271                     }
5272                 }
5273                 if(mm[1]){
5274                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5275                     q = q.replace(mm[1], "");
5276                 }
5277             }
5278             fn[fn.length] = "return nodup(n);\n}";
5279             
5280              /** 
5281               * list of variables that need from compression as they are used by eval.
5282              *  eval:var:batch 
5283              *  eval:var:nodup
5284              *  eval:var:byTag
5285              *  eval:var:ById
5286              *  eval:var:getNodes
5287              *  eval:var:quickId
5288              *  eval:var:mode
5289              *  eval:var:root
5290              *  eval:var:n
5291              *  eval:var:byClassName
5292              *  eval:var:byPseudo
5293              *  eval:var:byAttribute
5294              *  eval:var:attrValue
5295              * 
5296              **/ 
5297             eval(fn.join(""));
5298             return f;
5299         },
5300
5301         /**
5302          * Selects a group of elements.
5303          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5304          * @param {Node} root (optional) The start of the query (defaults to document).
5305          * @return {Array}
5306          */
5307         select : function(path, root, type){
5308             if(!root || root == document){
5309                 root = document;
5310             }
5311             if(typeof root == "string"){
5312                 root = document.getElementById(root);
5313             }
5314             var paths = path.split(",");
5315             var results = [];
5316             for(var i = 0, len = paths.length; i < len; i++){
5317                 var p = paths[i].replace(trimRe, "");
5318                 if(!cache[p]){
5319                     cache[p] = Roo.DomQuery.compile(p);
5320                     if(!cache[p]){
5321                         throw p + " is not a valid selector";
5322                     }
5323                 }
5324                 var result = cache[p](root);
5325                 if(result && result != document){
5326                     results = results.concat(result);
5327                 }
5328             }
5329             if(paths.length > 1){
5330                 return nodup(results);
5331             }
5332             return results;
5333         },
5334
5335         /**
5336          * Selects a single element.
5337          * @param {String} selector The selector/xpath query
5338          * @param {Node} root (optional) The start of the query (defaults to document).
5339          * @return {Element}
5340          */
5341         selectNode : function(path, root){
5342             return Roo.DomQuery.select(path, root)[0];
5343         },
5344
5345         /**
5346          * Selects the value of a node, optionally replacing null with the defaultValue.
5347          * @param {String} selector The selector/xpath query
5348          * @param {Node} root (optional) The start of the query (defaults to document).
5349          * @param {String} defaultValue
5350          */
5351         selectValue : function(path, root, defaultValue){
5352             path = path.replace(trimRe, "");
5353             if(!valueCache[path]){
5354                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5355             }
5356             var n = valueCache[path](root);
5357             n = n[0] ? n[0] : n;
5358             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5359             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5360         },
5361
5362         /**
5363          * Selects the value of a node, parsing integers and floats.
5364          * @param {String} selector The selector/xpath query
5365          * @param {Node} root (optional) The start of the query (defaults to document).
5366          * @param {Number} defaultValue
5367          * @return {Number}
5368          */
5369         selectNumber : function(path, root, defaultValue){
5370             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5371             return parseFloat(v);
5372         },
5373
5374         /**
5375          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5376          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5377          * @param {String} selector The simple selector to test
5378          * @return {Boolean}
5379          */
5380         is : function(el, ss){
5381             if(typeof el == "string"){
5382                 el = document.getElementById(el);
5383             }
5384             var isArray = (el instanceof Array);
5385             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5386             return isArray ? (result.length == el.length) : (result.length > 0);
5387         },
5388
5389         /**
5390          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5391          * @param {Array} el An array of elements to filter
5392          * @param {String} selector The simple selector to test
5393          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5394          * the selector instead of the ones that match
5395          * @return {Array}
5396          */
5397         filter : function(els, ss, nonMatches){
5398             ss = ss.replace(trimRe, "");
5399             if(!simpleCache[ss]){
5400                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5401             }
5402             var result = simpleCache[ss](els);
5403             return nonMatches ? quickDiff(result, els) : result;
5404         },
5405
5406         /**
5407          * Collection of matching regular expressions and code snippets.
5408          */
5409         matchers : [{
5410                 re: /^\.([\w-]+)/,
5411                 select: 'n = byClassName(n, null, " {1} ");'
5412             }, {
5413                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5414                 select: 'n = byPseudo(n, "{1}", "{2}");'
5415             },{
5416                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5417                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5418             }, {
5419                 re: /^#([\w-]+)/,
5420                 select: 'n = byId(n, null, "{1}");'
5421             },{
5422                 re: /^@([\w-]+)/,
5423                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5424             }
5425         ],
5426
5427         /**
5428          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5429          * 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;.
5430          */
5431         operators : {
5432             "=" : function(a, v){
5433                 return a == v;
5434             },
5435             "!=" : function(a, v){
5436                 return a != v;
5437             },
5438             "^=" : function(a, v){
5439                 return a && a.substr(0, v.length) == v;
5440             },
5441             "$=" : function(a, v){
5442                 return a && a.substr(a.length-v.length) == v;
5443             },
5444             "*=" : function(a, v){
5445                 return a && a.indexOf(v) !== -1;
5446             },
5447             "%=" : function(a, v){
5448                 return (a % v) == 0;
5449             },
5450             "|=" : function(a, v){
5451                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5452             },
5453             "~=" : function(a, v){
5454                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5455             }
5456         },
5457
5458         /**
5459          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5460          * and the argument (if any) supplied in the selector.
5461          */
5462         pseudos : {
5463             "first-child" : function(c){
5464                 var r = [], ri = -1, n;
5465                 for(var i = 0, ci; ci = n = c[i]; i++){
5466                     while((n = n.previousSibling) && n.nodeType != 1);
5467                     if(!n){
5468                         r[++ri] = ci;
5469                     }
5470                 }
5471                 return r;
5472             },
5473
5474             "last-child" : function(c){
5475                 var r = [], ri = -1, n;
5476                 for(var i = 0, ci; ci = n = c[i]; i++){
5477                     while((n = n.nextSibling) && n.nodeType != 1);
5478                     if(!n){
5479                         r[++ri] = ci;
5480                     }
5481                 }
5482                 return r;
5483             },
5484
5485             "nth-child" : function(c, a) {
5486                 var r = [], ri = -1;
5487                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5488                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5489                 for(var i = 0, n; n = c[i]; i++){
5490                     var pn = n.parentNode;
5491                     if (batch != pn._batch) {
5492                         var j = 0;
5493                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5494                             if(cn.nodeType == 1){
5495                                cn.nodeIndex = ++j;
5496                             }
5497                         }
5498                         pn._batch = batch;
5499                     }
5500                     if (f == 1) {
5501                         if (l == 0 || n.nodeIndex == l){
5502                             r[++ri] = n;
5503                         }
5504                     } else if ((n.nodeIndex + l) % f == 0){
5505                         r[++ri] = n;
5506                     }
5507                 }
5508
5509                 return r;
5510             },
5511
5512             "only-child" : function(c){
5513                 var r = [], ri = -1;;
5514                 for(var i = 0, ci; ci = c[i]; i++){
5515                     if(!prev(ci) && !next(ci)){
5516                         r[++ri] = ci;
5517                     }
5518                 }
5519                 return r;
5520             },
5521
5522             "empty" : function(c){
5523                 var r = [], ri = -1;
5524                 for(var i = 0, ci; ci = c[i]; i++){
5525                     var cns = ci.childNodes, j = 0, cn, empty = true;
5526                     while(cn = cns[j]){
5527                         ++j;
5528                         if(cn.nodeType == 1 || cn.nodeType == 3){
5529                             empty = false;
5530                             break;
5531                         }
5532                     }
5533                     if(empty){
5534                         r[++ri] = ci;
5535                     }
5536                 }
5537                 return r;
5538             },
5539
5540             "contains" : function(c, v){
5541                 var r = [], ri = -1;
5542                 for(var i = 0, ci; ci = c[i]; i++){
5543                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5544                         r[++ri] = ci;
5545                     }
5546                 }
5547                 return r;
5548             },
5549
5550             "nodeValue" : function(c, v){
5551                 var r = [], ri = -1;
5552                 for(var i = 0, ci; ci = c[i]; i++){
5553                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5554                         r[++ri] = ci;
5555                     }
5556                 }
5557                 return r;
5558             },
5559
5560             "checked" : function(c){
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     if(ci.checked == true){
5564                         r[++ri] = ci;
5565                     }
5566                 }
5567                 return r;
5568             },
5569
5570             "not" : function(c, ss){
5571                 return Roo.DomQuery.filter(c, ss, true);
5572             },
5573
5574             "odd" : function(c){
5575                 return this["nth-child"](c, "odd");
5576             },
5577
5578             "even" : function(c){
5579                 return this["nth-child"](c, "even");
5580             },
5581
5582             "nth" : function(c, a){
5583                 return c[a-1] || [];
5584             },
5585
5586             "first" : function(c){
5587                 return c[0] || [];
5588             },
5589
5590             "last" : function(c){
5591                 return c[c.length-1] || [];
5592             },
5593
5594             "has" : function(c, ss){
5595                 var s = Roo.DomQuery.select;
5596                 var r = [], ri = -1;
5597                 for(var i = 0, ci; ci = c[i]; i++){
5598                     if(s(ss, ci).length > 0){
5599                         r[++ri] = ci;
5600                     }
5601                 }
5602                 return r;
5603             },
5604
5605             "next" : function(c, ss){
5606                 var is = Roo.DomQuery.is;
5607                 var r = [], ri = -1;
5608                 for(var i = 0, ci; ci = c[i]; i++){
5609                     var n = next(ci);
5610                     if(n && is(n, ss)){
5611                         r[++ri] = ci;
5612                     }
5613                 }
5614                 return r;
5615             },
5616
5617             "prev" : function(c, ss){
5618                 var is = Roo.DomQuery.is;
5619                 var r = [], ri = -1;
5620                 for(var i = 0, ci; ci = c[i]; i++){
5621                     var n = prev(ci);
5622                     if(n && is(n, ss)){
5623                         r[++ri] = ci;
5624                     }
5625                 }
5626                 return r;
5627             }
5628         }
5629     };
5630 }();
5631
5632 /**
5633  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5634  * @param {String} path The selector/xpath query
5635  * @param {Node} root (optional) The start of the query (defaults to document).
5636  * @return {Array}
5637  * @member Roo
5638  * @method query
5639  */
5640 Roo.query = Roo.DomQuery.select;
5641 /*
5642  * Based on:
5643  * Ext JS Library 1.1.1
5644  * Copyright(c) 2006-2007, Ext JS, LLC.
5645  *
5646  * Originally Released Under LGPL - original licence link has changed is not relivant.
5647  *
5648  * Fork - LGPL
5649  * <script type="text/javascript">
5650  */
5651
5652 /**
5653  * @class Roo.util.Observable
5654  * Base class that provides a common interface for publishing events. Subclasses are expected to
5655  * to have a property "events" with all the events defined.<br>
5656  * For example:
5657  * <pre><code>
5658  Employee = function(name){
5659     this.name = name;
5660     this.addEvents({
5661         "fired" : true,
5662         "quit" : true
5663     });
5664  }
5665  Roo.extend(Employee, Roo.util.Observable);
5666 </code></pre>
5667  * @param {Object} config properties to use (incuding events / listeners)
5668  */
5669
5670 Roo.util.Observable = function(cfg){
5671     
5672     cfg = cfg|| {};
5673     this.addEvents(cfg.events || {});
5674     if (cfg.events) {
5675         delete cfg.events; // make sure
5676     }
5677      
5678     Roo.apply(this, cfg);
5679     
5680     if(this.listeners){
5681         this.on(this.listeners);
5682         delete this.listeners;
5683     }
5684 };
5685 Roo.util.Observable.prototype = {
5686     /** 
5687  * @cfg {Object} listeners  list of events and functions to call for this object, 
5688  * For example :
5689  * <pre><code>
5690     listeners :  { 
5691        'click' : function(e) {
5692            ..... 
5693         } ,
5694         .... 
5695     } 
5696   </code></pre>
5697  */
5698     
5699     
5700     /**
5701      * Fires the specified event with the passed parameters (minus the event name).
5702      * @param {String} eventName
5703      * @param {Object...} args Variable number of parameters are passed to handlers
5704      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5705      */
5706     fireEvent : function(){
5707         var ce = this.events[arguments[0].toLowerCase()];
5708         if(typeof ce == "object"){
5709             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5710         }else{
5711             return true;
5712         }
5713     },
5714
5715     // private
5716     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5717
5718     /**
5719      * Appends an event handler to this component
5720      * @param {String}   eventName The type of event to listen for
5721      * @param {Function} handler The method the event invokes
5722      * @param {Object}   scope (optional) The scope in which to execute the handler
5723      * function. The handler function's "this" context.
5724      * @param {Object}   options (optional) An object containing handler configuration
5725      * properties. This may contain any of the following properties:<ul>
5726      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5727      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5728      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5729      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5730      * by the specified number of milliseconds. If the event fires again within that time, the original
5731      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5732      * </ul><br>
5733      * <p>
5734      * <b>Combining Options</b><br>
5735      * Using the options argument, it is possible to combine different types of listeners:<br>
5736      * <br>
5737      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5738                 <pre><code>
5739                 el.on('click', this.onClick, this, {
5740                         single: true,
5741                 delay: 100,
5742                 forumId: 4
5743                 });
5744                 </code></pre>
5745      * <p>
5746      * <b>Attaching multiple handlers in 1 call</b><br>
5747      * The method also allows for a single argument to be passed which is a config object containing properties
5748      * which specify multiple handlers.
5749      * <pre><code>
5750                 el.on({
5751                         'click': {
5752                         fn: this.onClick,
5753                         scope: this,
5754                         delay: 100
5755                 }, 
5756                 'mouseover': {
5757                         fn: this.onMouseOver,
5758                         scope: this
5759                 },
5760                 'mouseout': {
5761                         fn: this.onMouseOut,
5762                         scope: this
5763                 }
5764                 });
5765                 </code></pre>
5766      * <p>
5767      * Or a shorthand syntax which passes the same scope object to all handlers:
5768         <pre><code>
5769                 el.on({
5770                         'click': this.onClick,
5771                 'mouseover': this.onMouseOver,
5772                 'mouseout': this.onMouseOut,
5773                 scope: this
5774                 });
5775                 </code></pre>
5776      */
5777     addListener : function(eventName, fn, scope, o){
5778         if(typeof eventName == "object"){
5779             o = eventName;
5780             for(var e in o){
5781                 if(this.filterOptRe.test(e)){
5782                     continue;
5783                 }
5784                 if(typeof o[e] == "function"){
5785                     // shared options
5786                     this.addListener(e, o[e], o.scope,  o);
5787                 }else{
5788                     // individual options
5789                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5790                 }
5791             }
5792             return;
5793         }
5794         o = (!o || typeof o == "boolean") ? {} : o;
5795         eventName = eventName.toLowerCase();
5796         var ce = this.events[eventName] || true;
5797         if(typeof ce == "boolean"){
5798             ce = new Roo.util.Event(this, eventName);
5799             this.events[eventName] = ce;
5800         }
5801         ce.addListener(fn, scope, o);
5802     },
5803
5804     /**
5805      * Removes a listener
5806      * @param {String}   eventName     The type of event to listen for
5807      * @param {Function} handler        The handler to remove
5808      * @param {Object}   scope  (optional) The scope (this object) for the handler
5809      */
5810     removeListener : function(eventName, fn, scope){
5811         var ce = this.events[eventName.toLowerCase()];
5812         if(typeof ce == "object"){
5813             ce.removeListener(fn, scope);
5814         }
5815     },
5816
5817     /**
5818      * Removes all listeners for this object
5819      */
5820     purgeListeners : function(){
5821         for(var evt in this.events){
5822             if(typeof this.events[evt] == "object"){
5823                  this.events[evt].clearListeners();
5824             }
5825         }
5826     },
5827
5828     relayEvents : function(o, events){
5829         var createHandler = function(ename){
5830             return function(){
5831                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5832             };
5833         };
5834         for(var i = 0, len = events.length; i < len; i++){
5835             var ename = events[i];
5836             if(!this.events[ename]){ this.events[ename] = true; };
5837             o.on(ename, createHandler(ename), this);
5838         }
5839     },
5840
5841     /**
5842      * Used to define events on this Observable
5843      * @param {Object} object The object with the events defined
5844      */
5845     addEvents : function(o){
5846         if(!this.events){
5847             this.events = {};
5848         }
5849         Roo.applyIf(this.events, o);
5850     },
5851
5852     /**
5853      * Checks to see if this object has any listeners for a specified event
5854      * @param {String} eventName The name of the event to check for
5855      * @return {Boolean} True if the event is being listened for, else false
5856      */
5857     hasListener : function(eventName){
5858         var e = this.events[eventName];
5859         return typeof e == "object" && e.listeners.length > 0;
5860     }
5861 };
5862 /**
5863  * Appends an event handler to this element (shorthand for addListener)
5864  * @param {String}   eventName     The type of event to listen for
5865  * @param {Function} handler        The method the event invokes
5866  * @param {Object}   scope (optional) The scope in which to execute the handler
5867  * function. The handler function's "this" context.
5868  * @param {Object}   options  (optional)
5869  * @method
5870  */
5871 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5872 /**
5873  * Removes a listener (shorthand for removeListener)
5874  * @param {String}   eventName     The type of event to listen for
5875  * @param {Function} handler        The handler to remove
5876  * @param {Object}   scope  (optional) The scope (this object) for the handler
5877  * @method
5878  */
5879 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5880
5881 /**
5882  * Starts capture on the specified Observable. All events will be passed
5883  * to the supplied function with the event name + standard signature of the event
5884  * <b>before</b> the event is fired. If the supplied function returns false,
5885  * the event will not fire.
5886  * @param {Observable} o The Observable to capture
5887  * @param {Function} fn The function to call
5888  * @param {Object} scope (optional) The scope (this object) for the fn
5889  * @static
5890  */
5891 Roo.util.Observable.capture = function(o, fn, scope){
5892     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5893 };
5894
5895 /**
5896  * Removes <b>all</b> added captures from the Observable.
5897  * @param {Observable} o The Observable to release
5898  * @static
5899  */
5900 Roo.util.Observable.releaseCapture = function(o){
5901     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5902 };
5903
5904 (function(){
5905
5906     var createBuffered = function(h, o, scope){
5907         var task = new Roo.util.DelayedTask();
5908         return function(){
5909             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5910         };
5911     };
5912
5913     var createSingle = function(h, e, fn, scope){
5914         return function(){
5915             e.removeListener(fn, scope);
5916             return h.apply(scope, arguments);
5917         };
5918     };
5919
5920     var createDelayed = function(h, o, scope){
5921         return function(){
5922             var args = Array.prototype.slice.call(arguments, 0);
5923             setTimeout(function(){
5924                 h.apply(scope, args);
5925             }, o.delay || 10);
5926         };
5927     };
5928
5929     Roo.util.Event = function(obj, name){
5930         this.name = name;
5931         this.obj = obj;
5932         this.listeners = [];
5933     };
5934
5935     Roo.util.Event.prototype = {
5936         addListener : function(fn, scope, options){
5937             var o = options || {};
5938             scope = scope || this.obj;
5939             if(!this.isListening(fn, scope)){
5940                 var l = {fn: fn, scope: scope, options: o};
5941                 var h = fn;
5942                 if(o.delay){
5943                     h = createDelayed(h, o, scope);
5944                 }
5945                 if(o.single){
5946                     h = createSingle(h, this, fn, scope);
5947                 }
5948                 if(o.buffer){
5949                     h = createBuffered(h, o, scope);
5950                 }
5951                 l.fireFn = h;
5952                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5953                     this.listeners.push(l);
5954                 }else{
5955                     this.listeners = this.listeners.slice(0);
5956                     this.listeners.push(l);
5957                 }
5958             }
5959         },
5960
5961         findListener : function(fn, scope){
5962             scope = scope || this.obj;
5963             var ls = this.listeners;
5964             for(var i = 0, len = ls.length; i < len; i++){
5965                 var l = ls[i];
5966                 if(l.fn == fn && l.scope == scope){
5967                     return i;
5968                 }
5969             }
5970             return -1;
5971         },
5972
5973         isListening : function(fn, scope){
5974             return this.findListener(fn, scope) != -1;
5975         },
5976
5977         removeListener : function(fn, scope){
5978             var index;
5979             if((index = this.findListener(fn, scope)) != -1){
5980                 if(!this.firing){
5981                     this.listeners.splice(index, 1);
5982                 }else{
5983                     this.listeners = this.listeners.slice(0);
5984                     this.listeners.splice(index, 1);
5985                 }
5986                 return true;
5987             }
5988             return false;
5989         },
5990
5991         clearListeners : function(){
5992             this.listeners = [];
5993         },
5994
5995         fire : function(){
5996             var ls = this.listeners, scope, len = ls.length;
5997             if(len > 0){
5998                 this.firing = true;
5999                 var args = Array.prototype.slice.call(arguments, 0);
6000                 for(var i = 0; i < len; i++){
6001                     var l = ls[i];
6002                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6003                         this.firing = false;
6004                         return false;
6005                     }
6006                 }
6007                 this.firing = false;
6008             }
6009             return true;
6010         }
6011     };
6012 })();/*
6013  * Based on:
6014  * Ext JS Library 1.1.1
6015  * Copyright(c) 2006-2007, Ext JS, LLC.
6016  *
6017  * Originally Released Under LGPL - original licence link has changed is not relivant.
6018  *
6019  * Fork - LGPL
6020  * <script type="text/javascript">
6021  */
6022
6023 /**
6024  * @class Roo.EventManager
6025  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6026  * several useful events directly.
6027  * See {@link Roo.EventObject} for more details on normalized event objects.
6028  * @singleton
6029  */
6030 Roo.EventManager = function(){
6031     var docReadyEvent, docReadyProcId, docReadyState = false;
6032     var resizeEvent, resizeTask, textEvent, textSize;
6033     var E = Roo.lib.Event;
6034     var D = Roo.lib.Dom;
6035
6036
6037     var fireDocReady = function(){
6038         if(!docReadyState){
6039             docReadyState = true;
6040             Roo.isReady = true;
6041             if(docReadyProcId){
6042                 clearInterval(docReadyProcId);
6043             }
6044             if(Roo.isGecko || Roo.isOpera) {
6045                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6046             }
6047             if(Roo.isIE){
6048                 var defer = document.getElementById("ie-deferred-loader");
6049                 if(defer){
6050                     defer.onreadystatechange = null;
6051                     defer.parentNode.removeChild(defer);
6052                 }
6053             }
6054             if(docReadyEvent){
6055                 docReadyEvent.fire();
6056                 docReadyEvent.clearListeners();
6057             }
6058         }
6059     };
6060     
6061     var initDocReady = function(){
6062         docReadyEvent = new Roo.util.Event();
6063         if(Roo.isGecko || Roo.isOpera) {
6064             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6065         }else if(Roo.isIE){
6066             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6067             var defer = document.getElementById("ie-deferred-loader");
6068             defer.onreadystatechange = function(){
6069                 if(this.readyState == "complete"){
6070                     fireDocReady();
6071                 }
6072             };
6073         }else if(Roo.isSafari){ 
6074             docReadyProcId = setInterval(function(){
6075                 var rs = document.readyState;
6076                 if(rs == "complete") {
6077                     fireDocReady();     
6078                  }
6079             }, 10);
6080         }
6081         // no matter what, make sure it fires on load
6082         E.on(window, "load", fireDocReady);
6083     };
6084
6085     var createBuffered = function(h, o){
6086         var task = new Roo.util.DelayedTask(h);
6087         return function(e){
6088             // create new event object impl so new events don't wipe out properties
6089             e = new Roo.EventObjectImpl(e);
6090             task.delay(o.buffer, h, null, [e]);
6091         };
6092     };
6093
6094     var createSingle = function(h, el, ename, fn){
6095         return function(e){
6096             Roo.EventManager.removeListener(el, ename, fn);
6097             h(e);
6098         };
6099     };
6100
6101     var createDelayed = function(h, o){
6102         return function(e){
6103             // create new event object impl so new events don't wipe out properties
6104             e = new Roo.EventObjectImpl(e);
6105             setTimeout(function(){
6106                 h(e);
6107             }, o.delay || 10);
6108         };
6109     };
6110
6111     var listen = function(element, ename, opt, fn, scope){
6112         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6113         fn = fn || o.fn; scope = scope || o.scope;
6114         var el = Roo.getDom(element);
6115         if(!el){
6116             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6117         }
6118         var h = function(e){
6119             e = Roo.EventObject.setEvent(e);
6120             var t;
6121             if(o.delegate){
6122                 t = e.getTarget(o.delegate, el);
6123                 if(!t){
6124                     return;
6125                 }
6126             }else{
6127                 t = e.target;
6128             }
6129             if(o.stopEvent === true){
6130                 e.stopEvent();
6131             }
6132             if(o.preventDefault === true){
6133                e.preventDefault();
6134             }
6135             if(o.stopPropagation === true){
6136                 e.stopPropagation();
6137             }
6138
6139             if(o.normalized === false){
6140                 e = e.browserEvent;
6141             }
6142
6143             fn.call(scope || el, e, t, o);
6144         };
6145         if(o.delay){
6146             h = createDelayed(h, o);
6147         }
6148         if(o.single){
6149             h = createSingle(h, el, ename, fn);
6150         }
6151         if(o.buffer){
6152             h = createBuffered(h, o);
6153         }
6154         fn._handlers = fn._handlers || [];
6155         fn._handlers.push([Roo.id(el), ename, h]);
6156
6157         E.on(el, ename, h);
6158         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6159             el.addEventListener("DOMMouseScroll", h, false);
6160             E.on(window, 'unload', function(){
6161                 el.removeEventListener("DOMMouseScroll", h, false);
6162             });
6163         }
6164         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6165             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6166         }
6167         return h;
6168     };
6169
6170     var stopListening = function(el, ename, fn){
6171         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6172         if(hds){
6173             for(var i = 0, len = hds.length; i < len; i++){
6174                 var h = hds[i];
6175                 if(h[0] == id && h[1] == ename){
6176                     hd = h[2];
6177                     hds.splice(i, 1);
6178                     break;
6179                 }
6180             }
6181         }
6182         E.un(el, ename, hd);
6183         el = Roo.getDom(el);
6184         if(ename == "mousewheel" && el.addEventListener){
6185             el.removeEventListener("DOMMouseScroll", hd, false);
6186         }
6187         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6188             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6189         }
6190     };
6191
6192     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6193     
6194     var pub = {
6195         
6196         
6197         /** 
6198          * Fix for doc tools
6199          * @scope Roo.EventManager
6200          */
6201         
6202         
6203         /** 
6204          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6205          * object with a Roo.EventObject
6206          * @param {Function} fn        The method the event invokes
6207          * @param {Object}   scope    An object that becomes the scope of the handler
6208          * @param {boolean}  override If true, the obj passed in becomes
6209          *                             the execution scope of the listener
6210          * @return {Function} The wrapped function
6211          * @deprecated
6212          */
6213         wrap : function(fn, scope, override){
6214             return function(e){
6215                 Roo.EventObject.setEvent(e);
6216                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6217             };
6218         },
6219         
6220         /**
6221      * Appends an event handler to an element (shorthand for addListener)
6222      * @param {String/HTMLElement}   element        The html element or id to assign the
6223      * @param {String}   eventName The type of event to listen for
6224      * @param {Function} handler The method the event invokes
6225      * @param {Object}   scope (optional) The scope in which to execute the handler
6226      * function. The handler function's "this" context.
6227      * @param {Object}   options (optional) An object containing handler configuration
6228      * properties. This may contain any of the following properties:<ul>
6229      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6230      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6231      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6232      * <li>preventDefault {Boolean} True to prevent the default action</li>
6233      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6234      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6235      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6236      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6237      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6238      * by the specified number of milliseconds. If the event fires again within that time, the original
6239      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6240      * </ul><br>
6241      * <p>
6242      * <b>Combining Options</b><br>
6243      * Using the options argument, it is possible to combine different types of listeners:<br>
6244      * <br>
6245      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6246      * Code:<pre><code>
6247 el.on('click', this.onClick, this, {
6248     single: true,
6249     delay: 100,
6250     stopEvent : true,
6251     forumId: 4
6252 });</code></pre>
6253      * <p>
6254      * <b>Attaching multiple handlers in 1 call</b><br>
6255       * The method also allows for a single argument to be passed which is a config object containing properties
6256      * which specify multiple handlers.
6257      * <p>
6258      * Code:<pre><code>
6259 el.on({
6260     'click' : {
6261         fn: this.onClick
6262         scope: this,
6263         delay: 100
6264     },
6265     'mouseover' : {
6266         fn: this.onMouseOver
6267         scope: this
6268     },
6269     'mouseout' : {
6270         fn: this.onMouseOut
6271         scope: this
6272     }
6273 });</code></pre>
6274      * <p>
6275      * Or a shorthand syntax:<br>
6276      * Code:<pre><code>
6277 el.on({
6278     'click' : this.onClick,
6279     'mouseover' : this.onMouseOver,
6280     'mouseout' : this.onMouseOut
6281     scope: this
6282 });</code></pre>
6283      */
6284         addListener : function(element, eventName, fn, scope, options){
6285             if(typeof eventName == "object"){
6286                 var o = eventName;
6287                 for(var e in o){
6288                     if(propRe.test(e)){
6289                         continue;
6290                     }
6291                     if(typeof o[e] == "function"){
6292                         // shared options
6293                         listen(element, e, o, o[e], o.scope);
6294                     }else{
6295                         // individual options
6296                         listen(element, e, o[e]);
6297                     }
6298                 }
6299                 return;
6300             }
6301             return listen(element, eventName, options, fn, scope);
6302         },
6303         
6304         /**
6305          * Removes an event handler
6306          *
6307          * @param {String/HTMLElement}   element        The id or html element to remove the 
6308          *                             event from
6309          * @param {String}   eventName     The type of event
6310          * @param {Function} fn
6311          * @return {Boolean} True if a listener was actually removed
6312          */
6313         removeListener : function(element, eventName, fn){
6314             return stopListening(element, eventName, fn);
6315         },
6316         
6317         /**
6318          * Fires when the document is ready (before onload and before images are loaded). Can be 
6319          * accessed shorthanded Roo.onReady().
6320          * @param {Function} fn        The method the event invokes
6321          * @param {Object}   scope    An  object that becomes the scope of the handler
6322          * @param {boolean}  options
6323          */
6324         onDocumentReady : function(fn, scope, options){
6325             if(docReadyState){ // if it already fired
6326                 docReadyEvent.addListener(fn, scope, options);
6327                 docReadyEvent.fire();
6328                 docReadyEvent.clearListeners();
6329                 return;
6330             }
6331             if(!docReadyEvent){
6332                 initDocReady();
6333             }
6334             docReadyEvent.addListener(fn, scope, options);
6335         },
6336         
6337         /**
6338          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6339          * @param {Function} fn        The method the event invokes
6340          * @param {Object}   scope    An object that becomes the scope of the handler
6341          * @param {boolean}  options
6342          */
6343         onWindowResize : function(fn, scope, options){
6344             if(!resizeEvent){
6345                 resizeEvent = new Roo.util.Event();
6346                 resizeTask = new Roo.util.DelayedTask(function(){
6347                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6348                 });
6349                 E.on(window, "resize", function(){
6350                     if(Roo.isIE){
6351                         resizeTask.delay(50);
6352                     }else{
6353                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6354                     }
6355                 });
6356             }
6357             resizeEvent.addListener(fn, scope, options);
6358         },
6359
6360         /**
6361          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6362          * @param {Function} fn        The method the event invokes
6363          * @param {Object}   scope    An object that becomes the scope of the handler
6364          * @param {boolean}  options
6365          */
6366         onTextResize : function(fn, scope, options){
6367             if(!textEvent){
6368                 textEvent = new Roo.util.Event();
6369                 var textEl = new Roo.Element(document.createElement('div'));
6370                 textEl.dom.className = 'x-text-resize';
6371                 textEl.dom.innerHTML = 'X';
6372                 textEl.appendTo(document.body);
6373                 textSize = textEl.dom.offsetHeight;
6374                 setInterval(function(){
6375                     if(textEl.dom.offsetHeight != textSize){
6376                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6377                     }
6378                 }, this.textResizeInterval);
6379             }
6380             textEvent.addListener(fn, scope, options);
6381         },
6382
6383         /**
6384          * Removes the passed window resize listener.
6385          * @param {Function} fn        The method the event invokes
6386          * @param {Object}   scope    The scope of handler
6387          */
6388         removeResizeListener : function(fn, scope){
6389             if(resizeEvent){
6390                 resizeEvent.removeListener(fn, scope);
6391             }
6392         },
6393
6394         // private
6395         fireResize : function(){
6396             if(resizeEvent){
6397                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6398             }   
6399         },
6400         /**
6401          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6402          */
6403         ieDeferSrc : false,
6404         /**
6405          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6406          */
6407         textResizeInterval : 50
6408     };
6409     
6410     /**
6411      * Fix for doc tools
6412      * @scopeAlias pub=Roo.EventManager
6413      */
6414     
6415      /**
6416      * Appends an event handler to an element (shorthand for addListener)
6417      * @param {String/HTMLElement}   element        The html element or id to assign the
6418      * @param {String}   eventName The type of event to listen for
6419      * @param {Function} handler The method the event invokes
6420      * @param {Object}   scope (optional) The scope in which to execute the handler
6421      * function. The handler function's "this" context.
6422      * @param {Object}   options (optional) An object containing handler configuration
6423      * properties. This may contain any of the following properties:<ul>
6424      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6425      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6426      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6427      * <li>preventDefault {Boolean} True to prevent the default action</li>
6428      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6429      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6430      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6431      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6432      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6433      * by the specified number of milliseconds. If the event fires again within that time, the original
6434      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6435      * </ul><br>
6436      * <p>
6437      * <b>Combining Options</b><br>
6438      * Using the options argument, it is possible to combine different types of listeners:<br>
6439      * <br>
6440      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6441      * Code:<pre><code>
6442 el.on('click', this.onClick, this, {
6443     single: true,
6444     delay: 100,
6445     stopEvent : true,
6446     forumId: 4
6447 });</code></pre>
6448      * <p>
6449      * <b>Attaching multiple handlers in 1 call</b><br>
6450       * The method also allows for a single argument to be passed which is a config object containing properties
6451      * which specify multiple handlers.
6452      * <p>
6453      * Code:<pre><code>
6454 el.on({
6455     'click' : {
6456         fn: this.onClick
6457         scope: this,
6458         delay: 100
6459     },
6460     'mouseover' : {
6461         fn: this.onMouseOver
6462         scope: this
6463     },
6464     'mouseout' : {
6465         fn: this.onMouseOut
6466         scope: this
6467     }
6468 });</code></pre>
6469      * <p>
6470      * Or a shorthand syntax:<br>
6471      * Code:<pre><code>
6472 el.on({
6473     'click' : this.onClick,
6474     'mouseover' : this.onMouseOver,
6475     'mouseout' : this.onMouseOut
6476     scope: this
6477 });</code></pre>
6478      */
6479     pub.on = pub.addListener;
6480     pub.un = pub.removeListener;
6481
6482     pub.stoppedMouseDownEvent = new Roo.util.Event();
6483     return pub;
6484 }();
6485 /**
6486   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6487   * @param {Function} fn        The method the event invokes
6488   * @param {Object}   scope    An  object that becomes the scope of the handler
6489   * @param {boolean}  override If true, the obj passed in becomes
6490   *                             the execution scope of the listener
6491   * @member Roo
6492   * @method onReady
6493  */
6494 Roo.onReady = Roo.EventManager.onDocumentReady;
6495
6496 Roo.onReady(function(){
6497     var bd = Roo.get(document.body);
6498     if(!bd){ return; }
6499
6500     var cls = [
6501             Roo.isIE ? "roo-ie"
6502             : Roo.isGecko ? "roo-gecko"
6503             : Roo.isOpera ? "roo-opera"
6504             : Roo.isSafari ? "roo-safari" : ""];
6505
6506     if(Roo.isMac){
6507         cls.push("roo-mac");
6508     }
6509     if(Roo.isLinux){
6510         cls.push("roo-linux");
6511     }
6512     if(Roo.isBorderBox){
6513         cls.push('roo-border-box');
6514     }
6515     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6516         var p = bd.dom.parentNode;
6517         if(p){
6518             p.className += ' roo-strict';
6519         }
6520     }
6521     bd.addClass(cls.join(' '));
6522 });
6523
6524 /**
6525  * @class Roo.EventObject
6526  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6527  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6528  * Example:
6529  * <pre><code>
6530  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6531     e.preventDefault();
6532     var target = e.getTarget();
6533     ...
6534  }
6535  var myDiv = Roo.get("myDiv");
6536  myDiv.on("click", handleClick);
6537  //or
6538  Roo.EventManager.on("myDiv", 'click', handleClick);
6539  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6540  </code></pre>
6541  * @singleton
6542  */
6543 Roo.EventObject = function(){
6544     
6545     var E = Roo.lib.Event;
6546     
6547     // safari keypress events for special keys return bad keycodes
6548     var safariKeys = {
6549         63234 : 37, // left
6550         63235 : 39, // right
6551         63232 : 38, // up
6552         63233 : 40, // down
6553         63276 : 33, // page up
6554         63277 : 34, // page down
6555         63272 : 46, // delete
6556         63273 : 36, // home
6557         63275 : 35  // end
6558     };
6559
6560     // normalize button clicks
6561     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6562                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6563
6564     Roo.EventObjectImpl = function(e){
6565         if(e){
6566             this.setEvent(e.browserEvent || e);
6567         }
6568     };
6569     Roo.EventObjectImpl.prototype = {
6570         /**
6571          * Used to fix doc tools.
6572          * @scope Roo.EventObject.prototype
6573          */
6574             
6575
6576         
6577         
6578         /** The normal browser event */
6579         browserEvent : null,
6580         /** The button pressed in a mouse event */
6581         button : -1,
6582         /** True if the shift key was down during the event */
6583         shiftKey : false,
6584         /** True if the control key was down during the event */
6585         ctrlKey : false,
6586         /** True if the alt key was down during the event */
6587         altKey : false,
6588
6589         /** Key constant 
6590         * @type Number */
6591         BACKSPACE : 8,
6592         /** Key constant 
6593         * @type Number */
6594         TAB : 9,
6595         /** Key constant 
6596         * @type Number */
6597         RETURN : 13,
6598         /** Key constant 
6599         * @type Number */
6600         ENTER : 13,
6601         /** Key constant 
6602         * @type Number */
6603         SHIFT : 16,
6604         /** Key constant 
6605         * @type Number */
6606         CONTROL : 17,
6607         /** Key constant 
6608         * @type Number */
6609         ESC : 27,
6610         /** Key constant 
6611         * @type Number */
6612         SPACE : 32,
6613         /** Key constant 
6614         * @type Number */
6615         PAGEUP : 33,
6616         /** Key constant 
6617         * @type Number */
6618         PAGEDOWN : 34,
6619         /** Key constant 
6620         * @type Number */
6621         END : 35,
6622         /** Key constant 
6623         * @type Number */
6624         HOME : 36,
6625         /** Key constant 
6626         * @type Number */
6627         LEFT : 37,
6628         /** Key constant 
6629         * @type Number */
6630         UP : 38,
6631         /** Key constant 
6632         * @type Number */
6633         RIGHT : 39,
6634         /** Key constant 
6635         * @type Number */
6636         DOWN : 40,
6637         /** Key constant 
6638         * @type Number */
6639         DELETE : 46,
6640         /** Key constant 
6641         * @type Number */
6642         F5 : 116,
6643
6644            /** @private */
6645         setEvent : function(e){
6646             if(e == this || (e && e.browserEvent)){ // already wrapped
6647                 return e;
6648             }
6649             this.browserEvent = e;
6650             if(e){
6651                 // normalize buttons
6652                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6653                 if(e.type == 'click' && this.button == -1){
6654                     this.button = 0;
6655                 }
6656                 this.type = e.type;
6657                 this.shiftKey = e.shiftKey;
6658                 // mac metaKey behaves like ctrlKey
6659                 this.ctrlKey = e.ctrlKey || e.metaKey;
6660                 this.altKey = e.altKey;
6661                 // in getKey these will be normalized for the mac
6662                 this.keyCode = e.keyCode;
6663                 // keyup warnings on firefox.
6664                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6665                 // cache the target for the delayed and or buffered events
6666                 this.target = E.getTarget(e);
6667                 // same for XY
6668                 this.xy = E.getXY(e);
6669             }else{
6670                 this.button = -1;
6671                 this.shiftKey = false;
6672                 this.ctrlKey = false;
6673                 this.altKey = false;
6674                 this.keyCode = 0;
6675                 this.charCode =0;
6676                 this.target = null;
6677                 this.xy = [0, 0];
6678             }
6679             return this;
6680         },
6681
6682         /**
6683          * Stop the event (preventDefault and stopPropagation)
6684          */
6685         stopEvent : function(){
6686             if(this.browserEvent){
6687                 if(this.browserEvent.type == 'mousedown'){
6688                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6689                 }
6690                 E.stopEvent(this.browserEvent);
6691             }
6692         },
6693
6694         /**
6695          * Prevents the browsers default handling of the event.
6696          */
6697         preventDefault : function(){
6698             if(this.browserEvent){
6699                 E.preventDefault(this.browserEvent);
6700             }
6701         },
6702
6703         /** @private */
6704         isNavKeyPress : function(){
6705             var k = this.keyCode;
6706             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6707             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6708         },
6709
6710         isSpecialKey : function(){
6711             var k = this.keyCode;
6712             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6713             (k == 16) || (k == 17) ||
6714             (k >= 18 && k <= 20) ||
6715             (k >= 33 && k <= 35) ||
6716             (k >= 36 && k <= 39) ||
6717             (k >= 44 && k <= 45);
6718         },
6719         /**
6720          * Cancels bubbling of the event.
6721          */
6722         stopPropagation : function(){
6723             if(this.browserEvent){
6724                 if(this.type == 'mousedown'){
6725                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6726                 }
6727                 E.stopPropagation(this.browserEvent);
6728             }
6729         },
6730
6731         /**
6732          * Gets the key code for the event.
6733          * @return {Number}
6734          */
6735         getCharCode : function(){
6736             return this.charCode || this.keyCode;
6737         },
6738
6739         /**
6740          * Returns a normalized keyCode for the event.
6741          * @return {Number} The key code
6742          */
6743         getKey : function(){
6744             var k = this.keyCode || this.charCode;
6745             return Roo.isSafari ? (safariKeys[k] || k) : k;
6746         },
6747
6748         /**
6749          * Gets the x coordinate of the event.
6750          * @return {Number}
6751          */
6752         getPageX : function(){
6753             return this.xy[0];
6754         },
6755
6756         /**
6757          * Gets the y coordinate of the event.
6758          * @return {Number}
6759          */
6760         getPageY : function(){
6761             return this.xy[1];
6762         },
6763
6764         /**
6765          * Gets the time of the event.
6766          * @return {Number}
6767          */
6768         getTime : function(){
6769             if(this.browserEvent){
6770                 return E.getTime(this.browserEvent);
6771             }
6772             return null;
6773         },
6774
6775         /**
6776          * Gets the page coordinates of the event.
6777          * @return {Array} The xy values like [x, y]
6778          */
6779         getXY : function(){
6780             return this.xy;
6781         },
6782
6783         /**
6784          * Gets the target for the event.
6785          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6786          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6787                 search as a number or element (defaults to 10 || document.body)
6788          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6789          * @return {HTMLelement}
6790          */
6791         getTarget : function(selector, maxDepth, returnEl){
6792             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6793         },
6794         /**
6795          * Gets the related target.
6796          * @return {HTMLElement}
6797          */
6798         getRelatedTarget : function(){
6799             if(this.browserEvent){
6800                 return E.getRelatedTarget(this.browserEvent);
6801             }
6802             return null;
6803         },
6804
6805         /**
6806          * Normalizes mouse wheel delta across browsers
6807          * @return {Number} The delta
6808          */
6809         getWheelDelta : function(){
6810             var e = this.browserEvent;
6811             var delta = 0;
6812             if(e.wheelDelta){ /* IE/Opera. */
6813                 delta = e.wheelDelta/120;
6814             }else if(e.detail){ /* Mozilla case. */
6815                 delta = -e.detail/3;
6816             }
6817             return delta;
6818         },
6819
6820         /**
6821          * Returns true if the control, meta, shift or alt key was pressed during this event.
6822          * @return {Boolean}
6823          */
6824         hasModifier : function(){
6825             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6826         },
6827
6828         /**
6829          * Returns true if the target of this event equals el or is a child of el
6830          * @param {String/HTMLElement/Element} el
6831          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6832          * @return {Boolean}
6833          */
6834         within : function(el, related){
6835             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6836             return t && Roo.fly(el).contains(t);
6837         },
6838
6839         getPoint : function(){
6840             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6841         }
6842     };
6843
6844     return new Roo.EventObjectImpl();
6845 }();
6846             
6847     /*
6848  * Based on:
6849  * Ext JS Library 1.1.1
6850  * Copyright(c) 2006-2007, Ext JS, LLC.
6851  *
6852  * Originally Released Under LGPL - original licence link has changed is not relivant.
6853  *
6854  * Fork - LGPL
6855  * <script type="text/javascript">
6856  */
6857
6858  
6859 // was in Composite Element!??!?!
6860  
6861 (function(){
6862     var D = Roo.lib.Dom;
6863     var E = Roo.lib.Event;
6864     var A = Roo.lib.Anim;
6865
6866     // local style camelizing for speed
6867     var propCache = {};
6868     var camelRe = /(-[a-z])/gi;
6869     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6870     var view = document.defaultView;
6871
6872 /**
6873  * @class Roo.Element
6874  * Represents an Element in the DOM.<br><br>
6875  * Usage:<br>
6876 <pre><code>
6877 var el = Roo.get("my-div");
6878
6879 // or with getEl
6880 var el = getEl("my-div");
6881
6882 // or with a DOM element
6883 var el = Roo.get(myDivElement);
6884 </code></pre>
6885  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6886  * each call instead of constructing a new one.<br><br>
6887  * <b>Animations</b><br />
6888  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6889  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6890 <pre>
6891 Option    Default   Description
6892 --------- --------  ---------------------------------------------
6893 duration  .35       The duration of the animation in seconds
6894 easing    easeOut   The YUI easing method
6895 callback  none      A function to execute when the anim completes
6896 scope     this      The scope (this) of the callback function
6897 </pre>
6898 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6899 * manipulate the animation. Here's an example:
6900 <pre><code>
6901 var el = Roo.get("my-div");
6902
6903 // no animation
6904 el.setWidth(100);
6905
6906 // default animation
6907 el.setWidth(100, true);
6908
6909 // animation with some options set
6910 el.setWidth(100, {
6911     duration: 1,
6912     callback: this.foo,
6913     scope: this
6914 });
6915
6916 // using the "anim" property to get the Anim object
6917 var opt = {
6918     duration: 1,
6919     callback: this.foo,
6920     scope: this
6921 };
6922 el.setWidth(100, opt);
6923 ...
6924 if(opt.anim.isAnimated()){
6925     opt.anim.stop();
6926 }
6927 </code></pre>
6928 * <b> Composite (Collections of) Elements</b><br />
6929  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6930  * @constructor Create a new Element directly.
6931  * @param {String/HTMLElement} element
6932  * @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).
6933  */
6934     Roo.Element = function(element, forceNew){
6935         var dom = typeof element == "string" ?
6936                 document.getElementById(element) : element;
6937         if(!dom){ // invalid id/element
6938             return null;
6939         }
6940         var id = dom.id;
6941         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6942             return Roo.Element.cache[id];
6943         }
6944
6945         /**
6946          * The DOM element
6947          * @type HTMLElement
6948          */
6949         this.dom = dom;
6950
6951         /**
6952          * The DOM element ID
6953          * @type String
6954          */
6955         this.id = id || Roo.id(dom);
6956     };
6957
6958     var El = Roo.Element;
6959
6960     El.prototype = {
6961         /**
6962          * The element's default display mode  (defaults to "")
6963          * @type String
6964          */
6965         originalDisplay : "",
6966
6967         visibilityMode : 1,
6968         /**
6969          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6970          * @type String
6971          */
6972         defaultUnit : "px",
6973         /**
6974          * Sets the element's visibility mode. When setVisible() is called it
6975          * will use this to determine whether to set the visibility or the display property.
6976          * @param visMode Element.VISIBILITY or Element.DISPLAY
6977          * @return {Roo.Element} this
6978          */
6979         setVisibilityMode : function(visMode){
6980             this.visibilityMode = visMode;
6981             return this;
6982         },
6983         /**
6984          * Convenience method for setVisibilityMode(Element.DISPLAY)
6985          * @param {String} display (optional) What to set display to when visible
6986          * @return {Roo.Element} this
6987          */
6988         enableDisplayMode : function(display){
6989             this.setVisibilityMode(El.DISPLAY);
6990             if(typeof display != "undefined") this.originalDisplay = display;
6991             return this;
6992         },
6993
6994         /**
6995          * 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)
6996          * @param {String} selector The simple selector to test
6997          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6998                 search as a number or element (defaults to 10 || document.body)
6999          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7000          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7001          */
7002         findParent : function(simpleSelector, maxDepth, returnEl){
7003             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7004             maxDepth = maxDepth || 50;
7005             if(typeof maxDepth != "number"){
7006                 stopEl = Roo.getDom(maxDepth);
7007                 maxDepth = 10;
7008             }
7009             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7010                 if(dq.is(p, simpleSelector)){
7011                     return returnEl ? Roo.get(p) : p;
7012                 }
7013                 depth++;
7014                 p = p.parentNode;
7015             }
7016             return null;
7017         },
7018
7019
7020         /**
7021          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7022          * @param {String} selector The simple selector to test
7023          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7024                 search as a number or element (defaults to 10 || document.body)
7025          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7026          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7027          */
7028         findParentNode : function(simpleSelector, maxDepth, returnEl){
7029             var p = Roo.fly(this.dom.parentNode, '_internal');
7030             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7031         },
7032
7033         /**
7034          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7035          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7036          * @param {String} selector The simple selector to test
7037          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7038                 search as a number or element (defaults to 10 || document.body)
7039          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7040          */
7041         up : function(simpleSelector, maxDepth){
7042             return this.findParentNode(simpleSelector, maxDepth, true);
7043         },
7044
7045
7046
7047         /**
7048          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7049          * @param {String} selector The simple selector to test
7050          * @return {Boolean} True if this element matches the selector, else false
7051          */
7052         is : function(simpleSelector){
7053             return Roo.DomQuery.is(this.dom, simpleSelector);
7054         },
7055
7056         /**
7057          * Perform animation on this element.
7058          * @param {Object} args The YUI animation control args
7059          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7060          * @param {Function} onComplete (optional) Function to call when animation completes
7061          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7062          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7063          * @return {Roo.Element} this
7064          */
7065         animate : function(args, duration, onComplete, easing, animType){
7066             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7067             return this;
7068         },
7069
7070         /*
7071          * @private Internal animation call
7072          */
7073         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7074             animType = animType || 'run';
7075             opt = opt || {};
7076             var anim = Roo.lib.Anim[animType](
7077                 this.dom, args,
7078                 (opt.duration || defaultDur) || .35,
7079                 (opt.easing || defaultEase) || 'easeOut',
7080                 function(){
7081                     Roo.callback(cb, this);
7082                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7083                 },
7084                 this
7085             );
7086             opt.anim = anim;
7087             return anim;
7088         },
7089
7090         // private legacy anim prep
7091         preanim : function(a, i){
7092             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7093         },
7094
7095         /**
7096          * Removes worthless text nodes
7097          * @param {Boolean} forceReclean (optional) By default the element
7098          * keeps track if it has been cleaned already so
7099          * you can call this over and over. However, if you update the element and
7100          * need to force a reclean, you can pass true.
7101          */
7102         clean : function(forceReclean){
7103             if(this.isCleaned && forceReclean !== true){
7104                 return this;
7105             }
7106             var ns = /\S/;
7107             var d = this.dom, n = d.firstChild, ni = -1;
7108             while(n){
7109                 var nx = n.nextSibling;
7110                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7111                     d.removeChild(n);
7112                 }else{
7113                     n.nodeIndex = ++ni;
7114                 }
7115                 n = nx;
7116             }
7117             this.isCleaned = true;
7118             return this;
7119         },
7120
7121         // private
7122         calcOffsetsTo : function(el){
7123             el = Roo.get(el);
7124             var d = el.dom;
7125             var restorePos = false;
7126             if(el.getStyle('position') == 'static'){
7127                 el.position('relative');
7128                 restorePos = true;
7129             }
7130             var x = 0, y =0;
7131             var op = this.dom;
7132             while(op && op != d && op.tagName != 'HTML'){
7133                 x+= op.offsetLeft;
7134                 y+= op.offsetTop;
7135                 op = op.offsetParent;
7136             }
7137             if(restorePos){
7138                 el.position('static');
7139             }
7140             return [x, y];
7141         },
7142
7143         /**
7144          * Scrolls this element into view within the passed container.
7145          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7146          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7147          * @return {Roo.Element} this
7148          */
7149         scrollIntoView : function(container, hscroll){
7150             var c = Roo.getDom(container) || document.body;
7151             var el = this.dom;
7152
7153             var o = this.calcOffsetsTo(c),
7154                 l = o[0],
7155                 t = o[1],
7156                 b = t+el.offsetHeight,
7157                 r = l+el.offsetWidth;
7158
7159             var ch = c.clientHeight;
7160             var ct = parseInt(c.scrollTop, 10);
7161             var cl = parseInt(c.scrollLeft, 10);
7162             var cb = ct + ch;
7163             var cr = cl + c.clientWidth;
7164
7165             if(t < ct){
7166                 c.scrollTop = t;
7167             }else if(b > cb){
7168                 c.scrollTop = b-ch;
7169             }
7170
7171             if(hscroll !== false){
7172                 if(l < cl){
7173                     c.scrollLeft = l;
7174                 }else if(r > cr){
7175                     c.scrollLeft = r-c.clientWidth;
7176                 }
7177             }
7178             return this;
7179         },
7180
7181         // private
7182         scrollChildIntoView : function(child, hscroll){
7183             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7184         },
7185
7186         /**
7187          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7188          * the new height may not be available immediately.
7189          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7190          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7191          * @param {Function} onComplete (optional) Function to call when animation completes
7192          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7193          * @return {Roo.Element} this
7194          */
7195         autoHeight : function(animate, duration, onComplete, easing){
7196             var oldHeight = this.getHeight();
7197             this.clip();
7198             this.setHeight(1); // force clipping
7199             setTimeout(function(){
7200                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7201                 if(!animate){
7202                     this.setHeight(height);
7203                     this.unclip();
7204                     if(typeof onComplete == "function"){
7205                         onComplete();
7206                     }
7207                 }else{
7208                     this.setHeight(oldHeight); // restore original height
7209                     this.setHeight(height, animate, duration, function(){
7210                         this.unclip();
7211                         if(typeof onComplete == "function") onComplete();
7212                     }.createDelegate(this), easing);
7213                 }
7214             }.createDelegate(this), 0);
7215             return this;
7216         },
7217
7218         /**
7219          * Returns true if this element is an ancestor of the passed element
7220          * @param {HTMLElement/String} el The element to check
7221          * @return {Boolean} True if this element is an ancestor of el, else false
7222          */
7223         contains : function(el){
7224             if(!el){return false;}
7225             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7226         },
7227
7228         /**
7229          * Checks whether the element is currently visible using both visibility and display properties.
7230          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7231          * @return {Boolean} True if the element is currently visible, else false
7232          */
7233         isVisible : function(deep) {
7234             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7235             if(deep !== true || !vis){
7236                 return vis;
7237             }
7238             var p = this.dom.parentNode;
7239             while(p && p.tagName.toLowerCase() != "body"){
7240                 if(!Roo.fly(p, '_isVisible').isVisible()){
7241                     return false;
7242                 }
7243                 p = p.parentNode;
7244             }
7245             return true;
7246         },
7247
7248         /**
7249          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7250          * @param {String} selector The CSS selector
7251          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7252          * @return {CompositeElement/CompositeElementLite} The composite element
7253          */
7254         select : function(selector, unique){
7255             return El.select(selector, unique, this.dom);
7256         },
7257
7258         /**
7259          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7260          * @param {String} selector The CSS selector
7261          * @return {Array} An array of the matched nodes
7262          */
7263         query : function(selector, unique){
7264             return Roo.DomQuery.select(selector, this.dom);
7265         },
7266
7267         /**
7268          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7269          * @param {String} selector The CSS selector
7270          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7271          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7272          */
7273         child : function(selector, returnDom){
7274             var n = Roo.DomQuery.selectNode(selector, this.dom);
7275             return returnDom ? n : Roo.get(n);
7276         },
7277
7278         /**
7279          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7280          * @param {String} selector The CSS selector
7281          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7282          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7283          */
7284         down : function(selector, returnDom){
7285             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7286             return returnDom ? n : Roo.get(n);
7287         },
7288
7289         /**
7290          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7291          * @param {String} group The group the DD object is member of
7292          * @param {Object} config The DD config object
7293          * @param {Object} overrides An object containing methods to override/implement on the DD object
7294          * @return {Roo.dd.DD} The DD object
7295          */
7296         initDD : function(group, config, overrides){
7297             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7298             return Roo.apply(dd, overrides);
7299         },
7300
7301         /**
7302          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7303          * @param {String} group The group the DDProxy object is member of
7304          * @param {Object} config The DDProxy config object
7305          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7306          * @return {Roo.dd.DDProxy} The DDProxy object
7307          */
7308         initDDProxy : function(group, config, overrides){
7309             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7310             return Roo.apply(dd, overrides);
7311         },
7312
7313         /**
7314          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7315          * @param {String} group The group the DDTarget object is member of
7316          * @param {Object} config The DDTarget config object
7317          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7318          * @return {Roo.dd.DDTarget} The DDTarget object
7319          */
7320         initDDTarget : function(group, config, overrides){
7321             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7322             return Roo.apply(dd, overrides);
7323         },
7324
7325         /**
7326          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7327          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7328          * @param {Boolean} visible Whether the element is visible
7329          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7330          * @return {Roo.Element} this
7331          */
7332          setVisible : function(visible, animate){
7333             if(!animate || !A){
7334                 if(this.visibilityMode == El.DISPLAY){
7335                     this.setDisplayed(visible);
7336                 }else{
7337                     this.fixDisplay();
7338                     this.dom.style.visibility = visible ? "visible" : "hidden";
7339                 }
7340             }else{
7341                 // closure for composites
7342                 var dom = this.dom;
7343                 var visMode = this.visibilityMode;
7344                 if(visible){
7345                     this.setOpacity(.01);
7346                     this.setVisible(true);
7347                 }
7348                 this.anim({opacity: { to: (visible?1:0) }},
7349                       this.preanim(arguments, 1),
7350                       null, .35, 'easeIn', function(){
7351                          if(!visible){
7352                              if(visMode == El.DISPLAY){
7353                                  dom.style.display = "none";
7354                              }else{
7355                                  dom.style.visibility = "hidden";
7356                              }
7357                              Roo.get(dom).setOpacity(1);
7358                          }
7359                      });
7360             }
7361             return this;
7362         },
7363
7364         /**
7365          * Returns true if display is not "none"
7366          * @return {Boolean}
7367          */
7368         isDisplayed : function() {
7369             return this.getStyle("display") != "none";
7370         },
7371
7372         /**
7373          * Toggles the element's visibility or display, depending on visibility mode.
7374          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7375          * @return {Roo.Element} this
7376          */
7377         toggle : function(animate){
7378             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7379             return this;
7380         },
7381
7382         /**
7383          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7384          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7385          * @return {Roo.Element} this
7386          */
7387         setDisplayed : function(value) {
7388             if(typeof value == "boolean"){
7389                value = value ? this.originalDisplay : "none";
7390             }
7391             this.setStyle("display", value);
7392             return this;
7393         },
7394
7395         /**
7396          * Tries to focus the element. Any exceptions are caught and ignored.
7397          * @return {Roo.Element} this
7398          */
7399         focus : function() {
7400             try{
7401                 this.dom.focus();
7402             }catch(e){}
7403             return this;
7404         },
7405
7406         /**
7407          * Tries to blur the element. Any exceptions are caught and ignored.
7408          * @return {Roo.Element} this
7409          */
7410         blur : function() {
7411             try{
7412                 this.dom.blur();
7413             }catch(e){}
7414             return this;
7415         },
7416
7417         /**
7418          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7419          * @param {String/Array} className The CSS class to add, or an array of classes
7420          * @return {Roo.Element} this
7421          */
7422         addClass : function(className){
7423             if(className instanceof Array){
7424                 for(var i = 0, len = className.length; i < len; i++) {
7425                     this.addClass(className[i]);
7426                 }
7427             }else{
7428                 if(className && !this.hasClass(className)){
7429                     this.dom.className = this.dom.className + " " + className;
7430                 }
7431             }
7432             return this;
7433         },
7434
7435         /**
7436          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7437          * @param {String/Array} className The CSS class to add, or an array of classes
7438          * @return {Roo.Element} this
7439          */
7440         radioClass : function(className){
7441             var siblings = this.dom.parentNode.childNodes;
7442             for(var i = 0; i < siblings.length; i++) {
7443                 var s = siblings[i];
7444                 if(s.nodeType == 1){
7445                     Roo.get(s).removeClass(className);
7446                 }
7447             }
7448             this.addClass(className);
7449             return this;
7450         },
7451
7452         /**
7453          * Removes one or more CSS classes from the element.
7454          * @param {String/Array} className The CSS class to remove, or an array of classes
7455          * @return {Roo.Element} this
7456          */
7457         removeClass : function(className){
7458             if(!className || !this.dom.className){
7459                 return this;
7460             }
7461             if(className instanceof Array){
7462                 for(var i = 0, len = className.length; i < len; i++) {
7463                     this.removeClass(className[i]);
7464                 }
7465             }else{
7466                 if(this.hasClass(className)){
7467                     var re = this.classReCache[className];
7468                     if (!re) {
7469                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7470                        this.classReCache[className] = re;
7471                     }
7472                     this.dom.className =
7473                         this.dom.className.replace(re, " ");
7474                 }
7475             }
7476             return this;
7477         },
7478
7479         // private
7480         classReCache: {},
7481
7482         /**
7483          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7484          * @param {String} className The CSS class to toggle
7485          * @return {Roo.Element} this
7486          */
7487         toggleClass : function(className){
7488             if(this.hasClass(className)){
7489                 this.removeClass(className);
7490             }else{
7491                 this.addClass(className);
7492             }
7493             return this;
7494         },
7495
7496         /**
7497          * Checks if the specified CSS class exists on this element's DOM node.
7498          * @param {String} className The CSS class to check for
7499          * @return {Boolean} True if the class exists, else false
7500          */
7501         hasClass : function(className){
7502             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7503         },
7504
7505         /**
7506          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7507          * @param {String} oldClassName The CSS class to replace
7508          * @param {String} newClassName The replacement CSS class
7509          * @return {Roo.Element} this
7510          */
7511         replaceClass : function(oldClassName, newClassName){
7512             this.removeClass(oldClassName);
7513             this.addClass(newClassName);
7514             return this;
7515         },
7516
7517         /**
7518          * Returns an object with properties matching the styles requested.
7519          * For example, el.getStyles('color', 'font-size', 'width') might return
7520          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7521          * @param {String} style1 A style name
7522          * @param {String} style2 A style name
7523          * @param {String} etc.
7524          * @return {Object} The style object
7525          */
7526         getStyles : function(){
7527             var a = arguments, len = a.length, r = {};
7528             for(var i = 0; i < len; i++){
7529                 r[a[i]] = this.getStyle(a[i]);
7530             }
7531             return r;
7532         },
7533
7534         /**
7535          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7536          * @param {String} property The style property whose value is returned.
7537          * @return {String} The current value of the style property for this element.
7538          */
7539         getStyle : function(){
7540             return view && view.getComputedStyle ?
7541                 function(prop){
7542                     var el = this.dom, v, cs, camel;
7543                     if(prop == 'float'){
7544                         prop = "cssFloat";
7545                     }
7546                     if(el.style && (v = el.style[prop])){
7547                         return v;
7548                     }
7549                     if(cs = view.getComputedStyle(el, "")){
7550                         if(!(camel = propCache[prop])){
7551                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7552                         }
7553                         return cs[camel];
7554                     }
7555                     return null;
7556                 } :
7557                 function(prop){
7558                     var el = this.dom, v, cs, camel;
7559                     if(prop == 'opacity'){
7560                         if(typeof el.style.filter == 'string'){
7561                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7562                             if(m){
7563                                 var fv = parseFloat(m[1]);
7564                                 if(!isNaN(fv)){
7565                                     return fv ? fv / 100 : 0;
7566                                 }
7567                             }
7568                         }
7569                         return 1;
7570                     }else if(prop == 'float'){
7571                         prop = "styleFloat";
7572                     }
7573                     if(!(camel = propCache[prop])){
7574                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7575                     }
7576                     if(v = el.style[camel]){
7577                         return v;
7578                     }
7579                     if(cs = el.currentStyle){
7580                         return cs[camel];
7581                     }
7582                     return null;
7583                 };
7584         }(),
7585
7586         /**
7587          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7588          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7589          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7590          * @return {Roo.Element} this
7591          */
7592         setStyle : function(prop, value){
7593             if(typeof prop == "string"){
7594                 
7595                 if (prop == 'float') {
7596                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7597                     return this;
7598                 }
7599                 
7600                 var camel;
7601                 if(!(camel = propCache[prop])){
7602                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7603                 }
7604                 
7605                 if(camel == 'opacity') {
7606                     this.setOpacity(value);
7607                 }else{
7608                     this.dom.style[camel] = value;
7609                 }
7610             }else{
7611                 for(var style in prop){
7612                     if(typeof prop[style] != "function"){
7613                        this.setStyle(style, prop[style]);
7614                     }
7615                 }
7616             }
7617             return this;
7618         },
7619
7620         /**
7621          * More flexible version of {@link #setStyle} for setting style properties.
7622          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7623          * a function which returns such a specification.
7624          * @return {Roo.Element} this
7625          */
7626         applyStyles : function(style){
7627             Roo.DomHelper.applyStyles(this.dom, style);
7628             return this;
7629         },
7630
7631         /**
7632           * 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).
7633           * @return {Number} The X position of the element
7634           */
7635         getX : function(){
7636             return D.getX(this.dom);
7637         },
7638
7639         /**
7640           * 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).
7641           * @return {Number} The Y position of the element
7642           */
7643         getY : function(){
7644             return D.getY(this.dom);
7645         },
7646
7647         /**
7648           * 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).
7649           * @return {Array} The XY position of the element
7650           */
7651         getXY : function(){
7652             return D.getXY(this.dom);
7653         },
7654
7655         /**
7656          * 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).
7657          * @param {Number} The X position of the element
7658          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7659          * @return {Roo.Element} this
7660          */
7661         setX : function(x, animate){
7662             if(!animate || !A){
7663                 D.setX(this.dom, x);
7664             }else{
7665                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7666             }
7667             return this;
7668         },
7669
7670         /**
7671          * 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).
7672          * @param {Number} The Y position of the element
7673          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7674          * @return {Roo.Element} this
7675          */
7676         setY : function(y, animate){
7677             if(!animate || !A){
7678                 D.setY(this.dom, y);
7679             }else{
7680                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7681             }
7682             return this;
7683         },
7684
7685         /**
7686          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7687          * @param {String} left The left CSS property value
7688          * @return {Roo.Element} this
7689          */
7690         setLeft : function(left){
7691             this.setStyle("left", this.addUnits(left));
7692             return this;
7693         },
7694
7695         /**
7696          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7697          * @param {String} top The top CSS property value
7698          * @return {Roo.Element} this
7699          */
7700         setTop : function(top){
7701             this.setStyle("top", this.addUnits(top));
7702             return this;
7703         },
7704
7705         /**
7706          * Sets the element's CSS right style.
7707          * @param {String} right The right CSS property value
7708          * @return {Roo.Element} this
7709          */
7710         setRight : function(right){
7711             this.setStyle("right", this.addUnits(right));
7712             return this;
7713         },
7714
7715         /**
7716          * Sets the element's CSS bottom style.
7717          * @param {String} bottom The bottom CSS property value
7718          * @return {Roo.Element} this
7719          */
7720         setBottom : function(bottom){
7721             this.setStyle("bottom", this.addUnits(bottom));
7722             return this;
7723         },
7724
7725         /**
7726          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7727          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7728          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7729          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7730          * @return {Roo.Element} this
7731          */
7732         setXY : function(pos, animate){
7733             if(!animate || !A){
7734                 D.setXY(this.dom, pos);
7735             }else{
7736                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7737             }
7738             return this;
7739         },
7740
7741         /**
7742          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7743          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7744          * @param {Number} x X value for new position (coordinates are page-based)
7745          * @param {Number} y Y value for new position (coordinates are page-based)
7746          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7747          * @return {Roo.Element} this
7748          */
7749         setLocation : function(x, y, animate){
7750             this.setXY([x, y], this.preanim(arguments, 2));
7751             return this;
7752         },
7753
7754         /**
7755          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7756          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7757          * @param {Number} x X value for new position (coordinates are page-based)
7758          * @param {Number} y Y value for new position (coordinates are page-based)
7759          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7760          * @return {Roo.Element} this
7761          */
7762         moveTo : function(x, y, animate){
7763             this.setXY([x, y], this.preanim(arguments, 2));
7764             return this;
7765         },
7766
7767         /**
7768          * Returns the region of the given element.
7769          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7770          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7771          */
7772         getRegion : function(){
7773             return D.getRegion(this.dom);
7774         },
7775
7776         /**
7777          * Returns the offset height of the element
7778          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7779          * @return {Number} The element's height
7780          */
7781         getHeight : function(contentHeight){
7782             var h = this.dom.offsetHeight || 0;
7783             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7784         },
7785
7786         /**
7787          * Returns the offset width of the element
7788          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7789          * @return {Number} The element's width
7790          */
7791         getWidth : function(contentWidth){
7792             var w = this.dom.offsetWidth || 0;
7793             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7794         },
7795
7796         /**
7797          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7798          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7799          * if a height has not been set using CSS.
7800          * @return {Number}
7801          */
7802         getComputedHeight : function(){
7803             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7804             if(!h){
7805                 h = parseInt(this.getStyle('height'), 10) || 0;
7806                 if(!this.isBorderBox()){
7807                     h += this.getFrameWidth('tb');
7808                 }
7809             }
7810             return h;
7811         },
7812
7813         /**
7814          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7815          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7816          * if a width has not been set using CSS.
7817          * @return {Number}
7818          */
7819         getComputedWidth : function(){
7820             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7821             if(!w){
7822                 w = parseInt(this.getStyle('width'), 10) || 0;
7823                 if(!this.isBorderBox()){
7824                     w += this.getFrameWidth('lr');
7825                 }
7826             }
7827             return w;
7828         },
7829
7830         /**
7831          * Returns the size of the element.
7832          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7833          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7834          */
7835         getSize : function(contentSize){
7836             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7837         },
7838
7839         /**
7840          * Returns the width and height of the viewport.
7841          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7842          */
7843         getViewSize : function(){
7844             var d = this.dom, doc = document, aw = 0, ah = 0;
7845             if(d == doc || d == doc.body){
7846                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7847             }else{
7848                 return {
7849                     width : d.clientWidth,
7850                     height: d.clientHeight
7851                 };
7852             }
7853         },
7854
7855         /**
7856          * Returns the value of the "value" attribute
7857          * @param {Boolean} asNumber true to parse the value as a number
7858          * @return {String/Number}
7859          */
7860         getValue : function(asNumber){
7861             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7862         },
7863
7864         // private
7865         adjustWidth : function(width){
7866             if(typeof width == "number"){
7867                 if(this.autoBoxAdjust && !this.isBorderBox()){
7868                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7869                 }
7870                 if(width < 0){
7871                     width = 0;
7872                 }
7873             }
7874             return width;
7875         },
7876
7877         // private
7878         adjustHeight : function(height){
7879             if(typeof height == "number"){
7880                if(this.autoBoxAdjust && !this.isBorderBox()){
7881                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7882                }
7883                if(height < 0){
7884                    height = 0;
7885                }
7886             }
7887             return height;
7888         },
7889
7890         /**
7891          * Set the width of the element
7892          * @param {Number} width The new width
7893          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7894          * @return {Roo.Element} this
7895          */
7896         setWidth : function(width, animate){
7897             width = this.adjustWidth(width);
7898             if(!animate || !A){
7899                 this.dom.style.width = this.addUnits(width);
7900             }else{
7901                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Set the height of the element
7908          * @param {Number} height The new height
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912          setHeight : function(height, animate){
7913             height = this.adjustHeight(height);
7914             if(!animate || !A){
7915                 this.dom.style.height = this.addUnits(height);
7916             }else{
7917                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7918             }
7919             return this;
7920         },
7921
7922         /**
7923          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7924          * @param {Number} width The new width
7925          * @param {Number} height The new height
7926          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7927          * @return {Roo.Element} this
7928          */
7929          setSize : function(width, height, animate){
7930             if(typeof width == "object"){ // in case of object from getSize()
7931                 height = width.height; width = width.width;
7932             }
7933             width = this.adjustWidth(width); height = this.adjustHeight(height);
7934             if(!animate || !A){
7935                 this.dom.style.width = this.addUnits(width);
7936                 this.dom.style.height = this.addUnits(height);
7937             }else{
7938                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7939             }
7940             return this;
7941         },
7942
7943         /**
7944          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7945          * @param {Number} x X value for new position (coordinates are page-based)
7946          * @param {Number} y Y value for new position (coordinates are page-based)
7947          * @param {Number} width The new width
7948          * @param {Number} height The new height
7949          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7950          * @return {Roo.Element} this
7951          */
7952         setBounds : function(x, y, width, height, animate){
7953             if(!animate || !A){
7954                 this.setSize(width, height);
7955                 this.setLocation(x, y);
7956             }else{
7957                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7958                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7959                               this.preanim(arguments, 4), 'motion');
7960             }
7961             return this;
7962         },
7963
7964         /**
7965          * 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.
7966          * @param {Roo.lib.Region} region The region to fill
7967          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7968          * @return {Roo.Element} this
7969          */
7970         setRegion : function(region, animate){
7971             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7972             return this;
7973         },
7974
7975         /**
7976          * Appends an event handler
7977          *
7978          * @param {String}   eventName     The type of event to append
7979          * @param {Function} fn        The method the event invokes
7980          * @param {Object} scope       (optional) The scope (this object) of the fn
7981          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7982          */
7983         addListener : function(eventName, fn, scope, options){
7984             if (this.dom) {
7985                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7986             }
7987         },
7988
7989         /**
7990          * Removes an event handler from this element
7991          * @param {String} eventName the type of event to remove
7992          * @param {Function} fn the method the event invokes
7993          * @return {Roo.Element} this
7994          */
7995         removeListener : function(eventName, fn){
7996             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7997             return this;
7998         },
7999
8000         /**
8001          * Removes all previous added listeners from this element
8002          * @return {Roo.Element} this
8003          */
8004         removeAllListeners : function(){
8005             E.purgeElement(this.dom);
8006             return this;
8007         },
8008
8009         relayEvent : function(eventName, observable){
8010             this.on(eventName, function(e){
8011                 observable.fireEvent(eventName, e);
8012             });
8013         },
8014
8015         /**
8016          * Set the opacity of the element
8017          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8018          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8019          * @return {Roo.Element} this
8020          */
8021          setOpacity : function(opacity, animate){
8022             if(!animate || !A){
8023                 var s = this.dom.style;
8024                 if(Roo.isIE){
8025                     s.zoom = 1;
8026                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8027                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8028                 }else{
8029                     s.opacity = opacity;
8030                 }
8031             }else{
8032                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8033             }
8034             return this;
8035         },
8036
8037         /**
8038          * Gets the left X coordinate
8039          * @param {Boolean} local True to get the local css position instead of page coordinate
8040          * @return {Number}
8041          */
8042         getLeft : function(local){
8043             if(!local){
8044                 return this.getX();
8045             }else{
8046                 return parseInt(this.getStyle("left"), 10) || 0;
8047             }
8048         },
8049
8050         /**
8051          * Gets the right X coordinate of the element (element X position + element width)
8052          * @param {Boolean} local True to get the local css position instead of page coordinate
8053          * @return {Number}
8054          */
8055         getRight : function(local){
8056             if(!local){
8057                 return this.getX() + this.getWidth();
8058             }else{
8059                 return (this.getLeft(true) + this.getWidth()) || 0;
8060             }
8061         },
8062
8063         /**
8064          * Gets the top Y coordinate
8065          * @param {Boolean} local True to get the local css position instead of page coordinate
8066          * @return {Number}
8067          */
8068         getTop : function(local) {
8069             if(!local){
8070                 return this.getY();
8071             }else{
8072                 return parseInt(this.getStyle("top"), 10) || 0;
8073             }
8074         },
8075
8076         /**
8077          * Gets the bottom Y coordinate of the element (element Y position + element height)
8078          * @param {Boolean} local True to get the local css position instead of page coordinate
8079          * @return {Number}
8080          */
8081         getBottom : function(local){
8082             if(!local){
8083                 return this.getY() + this.getHeight();
8084             }else{
8085                 return (this.getTop(true) + this.getHeight()) || 0;
8086             }
8087         },
8088
8089         /**
8090         * Initializes positioning on this element. If a desired position is not passed, it will make the
8091         * the element positioned relative IF it is not already positioned.
8092         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8093         * @param {Number} zIndex (optional) The zIndex to apply
8094         * @param {Number} x (optional) Set the page X position
8095         * @param {Number} y (optional) Set the page Y position
8096         */
8097         position : function(pos, zIndex, x, y){
8098             if(!pos){
8099                if(this.getStyle('position') == 'static'){
8100                    this.setStyle('position', 'relative');
8101                }
8102             }else{
8103                 this.setStyle("position", pos);
8104             }
8105             if(zIndex){
8106                 this.setStyle("z-index", zIndex);
8107             }
8108             if(x !== undefined && y !== undefined){
8109                 this.setXY([x, y]);
8110             }else if(x !== undefined){
8111                 this.setX(x);
8112             }else if(y !== undefined){
8113                 this.setY(y);
8114             }
8115         },
8116
8117         /**
8118         * Clear positioning back to the default when the document was loaded
8119         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8120         * @return {Roo.Element} this
8121          */
8122         clearPositioning : function(value){
8123             value = value ||'';
8124             this.setStyle({
8125                 "left": value,
8126                 "right": value,
8127                 "top": value,
8128                 "bottom": value,
8129                 "z-index": "",
8130                 "position" : "static"
8131             });
8132             return this;
8133         },
8134
8135         /**
8136         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8137         * snapshot before performing an update and then restoring the element.
8138         * @return {Object}
8139         */
8140         getPositioning : function(){
8141             var l = this.getStyle("left");
8142             var t = this.getStyle("top");
8143             return {
8144                 "position" : this.getStyle("position"),
8145                 "left" : l,
8146                 "right" : l ? "" : this.getStyle("right"),
8147                 "top" : t,
8148                 "bottom" : t ? "" : this.getStyle("bottom"),
8149                 "z-index" : this.getStyle("z-index")
8150             };
8151         },
8152
8153         /**
8154          * Gets the width of the border(s) for the specified side(s)
8155          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8156          * passing lr would get the border (l)eft width + the border (r)ight width.
8157          * @return {Number} The width of the sides passed added together
8158          */
8159         getBorderWidth : function(side){
8160             return this.addStyles(side, El.borders);
8161         },
8162
8163         /**
8164          * Gets the width of the padding(s) for the specified side(s)
8165          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8166          * passing lr would get the padding (l)eft + the padding (r)ight.
8167          * @return {Number} The padding of the sides passed added together
8168          */
8169         getPadding : function(side){
8170             return this.addStyles(side, El.paddings);
8171         },
8172
8173         /**
8174         * Set positioning with an object returned by getPositioning().
8175         * @param {Object} posCfg
8176         * @return {Roo.Element} this
8177          */
8178         setPositioning : function(pc){
8179             this.applyStyles(pc);
8180             if(pc.right == "auto"){
8181                 this.dom.style.right = "";
8182             }
8183             if(pc.bottom == "auto"){
8184                 this.dom.style.bottom = "";
8185             }
8186             return this;
8187         },
8188
8189         // private
8190         fixDisplay : function(){
8191             if(this.getStyle("display") == "none"){
8192                 this.setStyle("visibility", "hidden");
8193                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8194                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8195                     this.setStyle("display", "block");
8196                 }
8197             }
8198         },
8199
8200         /**
8201          * Quick set left and top adding default units
8202          * @param {String} left The left CSS property value
8203          * @param {String} top The top CSS property value
8204          * @return {Roo.Element} this
8205          */
8206          setLeftTop : function(left, top){
8207             this.dom.style.left = this.addUnits(left);
8208             this.dom.style.top = this.addUnits(top);
8209             return this;
8210         },
8211
8212         /**
8213          * Move this element relative to its current position.
8214          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8215          * @param {Number} distance How far to move the element in pixels
8216          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8217          * @return {Roo.Element} this
8218          */
8219          move : function(direction, distance, animate){
8220             var xy = this.getXY();
8221             direction = direction.toLowerCase();
8222             switch(direction){
8223                 case "l":
8224                 case "left":
8225                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8226                     break;
8227                case "r":
8228                case "right":
8229                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8230                     break;
8231                case "t":
8232                case "top":
8233                case "up":
8234                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8235                     break;
8236                case "b":
8237                case "bottom":
8238                case "down":
8239                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8240                     break;
8241             }
8242             return this;
8243         },
8244
8245         /**
8246          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8247          * @return {Roo.Element} this
8248          */
8249         clip : function(){
8250             if(!this.isClipped){
8251                this.isClipped = true;
8252                this.originalClip = {
8253                    "o": this.getStyle("overflow"),
8254                    "x": this.getStyle("overflow-x"),
8255                    "y": this.getStyle("overflow-y")
8256                };
8257                this.setStyle("overflow", "hidden");
8258                this.setStyle("overflow-x", "hidden");
8259                this.setStyle("overflow-y", "hidden");
8260             }
8261             return this;
8262         },
8263
8264         /**
8265          *  Return clipping (overflow) to original clipping before clip() was called
8266          * @return {Roo.Element} this
8267          */
8268         unclip : function(){
8269             if(this.isClipped){
8270                 this.isClipped = false;
8271                 var o = this.originalClip;
8272                 if(o.o){this.setStyle("overflow", o.o);}
8273                 if(o.x){this.setStyle("overflow-x", o.x);}
8274                 if(o.y){this.setStyle("overflow-y", o.y);}
8275             }
8276             return this;
8277         },
8278
8279
8280         /**
8281          * Gets the x,y coordinates specified by the anchor position on the element.
8282          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8283          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8284          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8285          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8286          * @return {Array} [x, y] An array containing the element's x and y coordinates
8287          */
8288         getAnchorXY : function(anchor, local, s){
8289             //Passing a different size is useful for pre-calculating anchors,
8290             //especially for anchored animations that change the el size.
8291
8292             var w, h, vp = false;
8293             if(!s){
8294                 var d = this.dom;
8295                 if(d == document.body || d == document){
8296                     vp = true;
8297                     w = D.getViewWidth(); h = D.getViewHeight();
8298                 }else{
8299                     w = this.getWidth(); h = this.getHeight();
8300                 }
8301             }else{
8302                 w = s.width;  h = s.height;
8303             }
8304             var x = 0, y = 0, r = Math.round;
8305             switch((anchor || "tl").toLowerCase()){
8306                 case "c":
8307                     x = r(w*.5);
8308                     y = r(h*.5);
8309                 break;
8310                 case "t":
8311                     x = r(w*.5);
8312                     y = 0;
8313                 break;
8314                 case "l":
8315                     x = 0;
8316                     y = r(h*.5);
8317                 break;
8318                 case "r":
8319                     x = w;
8320                     y = r(h*.5);
8321                 break;
8322                 case "b":
8323                     x = r(w*.5);
8324                     y = h;
8325                 break;
8326                 case "tl":
8327                     x = 0;
8328                     y = 0;
8329                 break;
8330                 case "bl":
8331                     x = 0;
8332                     y = h;
8333                 break;
8334                 case "br":
8335                     x = w;
8336                     y = h;
8337                 break;
8338                 case "tr":
8339                     x = w;
8340                     y = 0;
8341                 break;
8342             }
8343             if(local === true){
8344                 return [x, y];
8345             }
8346             if(vp){
8347                 var sc = this.getScroll();
8348                 return [x + sc.left, y + sc.top];
8349             }
8350             //Add the element's offset xy
8351             var o = this.getXY();
8352             return [x+o[0], y+o[1]];
8353         },
8354
8355         /**
8356          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8357          * supported position values.
8358          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8359          * @param {String} position The position to align to.
8360          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8361          * @return {Array} [x, y]
8362          */
8363         getAlignToXY : function(el, p, o){
8364             el = Roo.get(el);
8365             var d = this.dom;
8366             if(!el.dom){
8367                 throw "Element.alignTo with an element that doesn't exist";
8368             }
8369             var c = false; //constrain to viewport
8370             var p1 = "", p2 = "";
8371             o = o || [0,0];
8372
8373             if(!p){
8374                 p = "tl-bl";
8375             }else if(p == "?"){
8376                 p = "tl-bl?";
8377             }else if(p.indexOf("-") == -1){
8378                 p = "tl-" + p;
8379             }
8380             p = p.toLowerCase();
8381             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8382             if(!m){
8383                throw "Element.alignTo with an invalid alignment " + p;
8384             }
8385             p1 = m[1]; p2 = m[2]; c = !!m[3];
8386
8387             //Subtract the aligned el's internal xy from the target's offset xy
8388             //plus custom offset to get the aligned el's new offset xy
8389             var a1 = this.getAnchorXY(p1, true);
8390             var a2 = el.getAnchorXY(p2, false);
8391             var x = a2[0] - a1[0] + o[0];
8392             var y = a2[1] - a1[1] + o[1];
8393             if(c){
8394                 //constrain the aligned el to viewport if necessary
8395                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8396                 // 5px of margin for ie
8397                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8398
8399                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8400                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8401                 //otherwise swap the aligned el to the opposite border of the target.
8402                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8403                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8404                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8405                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8406
8407                var doc = document;
8408                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8409                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8410
8411                if((x+w) > dw + scrollX){
8412                     x = swapX ? r.left-w : dw+scrollX-w;
8413                 }
8414                if(x < scrollX){
8415                    x = swapX ? r.right : scrollX;
8416                }
8417                if((y+h) > dh + scrollY){
8418                     y = swapY ? r.top-h : dh+scrollY-h;
8419                 }
8420                if (y < scrollY){
8421                    y = swapY ? r.bottom : scrollY;
8422                }
8423             }
8424             return [x,y];
8425         },
8426
8427         // private
8428         getConstrainToXY : function(){
8429             var os = {top:0, left:0, bottom:0, right: 0};
8430
8431             return function(el, local, offsets, proposedXY){
8432                 el = Roo.get(el);
8433                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8434
8435                 var vw, vh, vx = 0, vy = 0;
8436                 if(el.dom == document.body || el.dom == document){
8437                     vw = Roo.lib.Dom.getViewWidth();
8438                     vh = Roo.lib.Dom.getViewHeight();
8439                 }else{
8440                     vw = el.dom.clientWidth;
8441                     vh = el.dom.clientHeight;
8442                     if(!local){
8443                         var vxy = el.getXY();
8444                         vx = vxy[0];
8445                         vy = vxy[1];
8446                     }
8447                 }
8448
8449                 var s = el.getScroll();
8450
8451                 vx += offsets.left + s.left;
8452                 vy += offsets.top + s.top;
8453
8454                 vw -= offsets.right;
8455                 vh -= offsets.bottom;
8456
8457                 var vr = vx+vw;
8458                 var vb = vy+vh;
8459
8460                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8461                 var x = xy[0], y = xy[1];
8462                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8463
8464                 // only move it if it needs it
8465                 var moved = false;
8466
8467                 // first validate right/bottom
8468                 if((x + w) > vr){
8469                     x = vr - w;
8470                     moved = true;
8471                 }
8472                 if((y + h) > vb){
8473                     y = vb - h;
8474                     moved = true;
8475                 }
8476                 // then make sure top/left isn't negative
8477                 if(x < vx){
8478                     x = vx;
8479                     moved = true;
8480                 }
8481                 if(y < vy){
8482                     y = vy;
8483                     moved = true;
8484                 }
8485                 return moved ? [x, y] : false;
8486             };
8487         }(),
8488
8489         // private
8490         adjustForConstraints : function(xy, parent, offsets){
8491             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8492         },
8493
8494         /**
8495          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8496          * document it aligns it to the viewport.
8497          * The position parameter is optional, and can be specified in any one of the following formats:
8498          * <ul>
8499          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8500          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8501          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8502          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8503          *   <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
8504          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8505          * </ul>
8506          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8507          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8508          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8509          * that specified in order to enforce the viewport constraints.
8510          * Following are all of the supported anchor positions:
8511     <pre>
8512     Value  Description
8513     -----  -----------------------------
8514     tl     The top left corner (default)
8515     t      The center of the top edge
8516     tr     The top right corner
8517     l      The center of the left edge
8518     c      In the center of the element
8519     r      The center of the right edge
8520     bl     The bottom left corner
8521     b      The center of the bottom edge
8522     br     The bottom right corner
8523     </pre>
8524     Example Usage:
8525     <pre><code>
8526     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8527     el.alignTo("other-el");
8528
8529     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8530     el.alignTo("other-el", "tr?");
8531
8532     // align the bottom right corner of el with the center left edge of other-el
8533     el.alignTo("other-el", "br-l?");
8534
8535     // align the center of el with the bottom left corner of other-el and
8536     // adjust the x position by -6 pixels (and the y position by 0)
8537     el.alignTo("other-el", "c-bl", [-6, 0]);
8538     </code></pre>
8539          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8540          * @param {String} position The position to align to.
8541          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8542          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8543          * @return {Roo.Element} this
8544          */
8545         alignTo : function(element, position, offsets, animate){
8546             var xy = this.getAlignToXY(element, position, offsets);
8547             this.setXY(xy, this.preanim(arguments, 3));
8548             return this;
8549         },
8550
8551         /**
8552          * Anchors an element to another element and realigns it when the window is resized.
8553          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8554          * @param {String} position The position to align to.
8555          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8556          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8557          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8558          * is a number, it is used as the buffer delay (defaults to 50ms).
8559          * @param {Function} callback The function to call after the animation finishes
8560          * @return {Roo.Element} this
8561          */
8562         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8563             var action = function(){
8564                 this.alignTo(el, alignment, offsets, animate);
8565                 Roo.callback(callback, this);
8566             };
8567             Roo.EventManager.onWindowResize(action, this);
8568             var tm = typeof monitorScroll;
8569             if(tm != 'undefined'){
8570                 Roo.EventManager.on(window, 'scroll', action, this,
8571                     {buffer: tm == 'number' ? monitorScroll : 50});
8572             }
8573             action.call(this); // align immediately
8574             return this;
8575         },
8576         /**
8577          * Clears any opacity settings from this element. Required in some cases for IE.
8578          * @return {Roo.Element} this
8579          */
8580         clearOpacity : function(){
8581             if (window.ActiveXObject) {
8582                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8583                     this.dom.style.filter = "";
8584                 }
8585             } else {
8586                 this.dom.style.opacity = "";
8587                 this.dom.style["-moz-opacity"] = "";
8588                 this.dom.style["-khtml-opacity"] = "";
8589             }
8590             return this;
8591         },
8592
8593         /**
8594          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8595          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8596          * @return {Roo.Element} this
8597          */
8598         hide : function(animate){
8599             this.setVisible(false, this.preanim(arguments, 0));
8600             return this;
8601         },
8602
8603         /**
8604         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8605         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8606          * @return {Roo.Element} this
8607          */
8608         show : function(animate){
8609             this.setVisible(true, this.preanim(arguments, 0));
8610             return this;
8611         },
8612
8613         /**
8614          * @private Test if size has a unit, otherwise appends the default
8615          */
8616         addUnits : function(size){
8617             return Roo.Element.addUnits(size, this.defaultUnit);
8618         },
8619
8620         /**
8621          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8622          * @return {Roo.Element} this
8623          */
8624         beginMeasure : function(){
8625             var el = this.dom;
8626             if(el.offsetWidth || el.offsetHeight){
8627                 return this; // offsets work already
8628             }
8629             var changed = [];
8630             var p = this.dom, b = document.body; // start with this element
8631             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8632                 var pe = Roo.get(p);
8633                 if(pe.getStyle('display') == 'none'){
8634                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8635                     p.style.visibility = "hidden";
8636                     p.style.display = "block";
8637                 }
8638                 p = p.parentNode;
8639             }
8640             this._measureChanged = changed;
8641             return this;
8642
8643         },
8644
8645         /**
8646          * Restores displays to before beginMeasure was called
8647          * @return {Roo.Element} this
8648          */
8649         endMeasure : function(){
8650             var changed = this._measureChanged;
8651             if(changed){
8652                 for(var i = 0, len = changed.length; i < len; i++) {
8653                     var r = changed[i];
8654                     r.el.style.visibility = r.visibility;
8655                     r.el.style.display = "none";
8656                 }
8657                 this._measureChanged = null;
8658             }
8659             return this;
8660         },
8661
8662         /**
8663         * Update the innerHTML of this element, optionally searching for and processing scripts
8664         * @param {String} html The new HTML
8665         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8666         * @param {Function} callback For async script loading you can be noticed when the update completes
8667         * @return {Roo.Element} this
8668          */
8669         update : function(html, loadScripts, callback){
8670             if(typeof html == "undefined"){
8671                 html = "";
8672             }
8673             if(loadScripts !== true){
8674                 this.dom.innerHTML = html;
8675                 if(typeof callback == "function"){
8676                     callback();
8677                 }
8678                 return this;
8679             }
8680             var id = Roo.id();
8681             var dom = this.dom;
8682
8683             html += '<span id="' + id + '"></span>';
8684
8685             E.onAvailable(id, function(){
8686                 var hd = document.getElementsByTagName("head")[0];
8687                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8688                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8689                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8690
8691                 var match;
8692                 while(match = re.exec(html)){
8693                     var attrs = match[1];
8694                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8695                     if(srcMatch && srcMatch[2]){
8696                        var s = document.createElement("script");
8697                        s.src = srcMatch[2];
8698                        var typeMatch = attrs.match(typeRe);
8699                        if(typeMatch && typeMatch[2]){
8700                            s.type = typeMatch[2];
8701                        }
8702                        hd.appendChild(s);
8703                     }else if(match[2] && match[2].length > 0){
8704                         if(window.execScript) {
8705                            window.execScript(match[2]);
8706                         } else {
8707                             /**
8708                              * eval:var:id
8709                              * eval:var:dom
8710                              * eval:var:html
8711                              * 
8712                              */
8713                            window.eval(match[2]);
8714                         }
8715                     }
8716                 }
8717                 var el = document.getElementById(id);
8718                 if(el){el.parentNode.removeChild(el);}
8719                 if(typeof callback == "function"){
8720                     callback();
8721                 }
8722             });
8723             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8724             return this;
8725         },
8726
8727         /**
8728          * Direct access to the UpdateManager update() method (takes the same parameters).
8729          * @param {String/Function} url The url for this request or a function to call to get the url
8730          * @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}
8731          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8732          * @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.
8733          * @return {Roo.Element} this
8734          */
8735         load : function(){
8736             var um = this.getUpdateManager();
8737             um.update.apply(um, arguments);
8738             return this;
8739         },
8740
8741         /**
8742         * Gets this element's UpdateManager
8743         * @return {Roo.UpdateManager} The UpdateManager
8744         */
8745         getUpdateManager : function(){
8746             if(!this.updateManager){
8747                 this.updateManager = new Roo.UpdateManager(this);
8748             }
8749             return this.updateManager;
8750         },
8751
8752         /**
8753          * Disables text selection for this element (normalized across browsers)
8754          * @return {Roo.Element} this
8755          */
8756         unselectable : function(){
8757             this.dom.unselectable = "on";
8758             this.swallowEvent("selectstart", true);
8759             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8760             this.addClass("x-unselectable");
8761             return this;
8762         },
8763
8764         /**
8765         * Calculates the x, y to center this element on the screen
8766         * @return {Array} The x, y values [x, y]
8767         */
8768         getCenterXY : function(){
8769             return this.getAlignToXY(document, 'c-c');
8770         },
8771
8772         /**
8773         * Centers the Element in either the viewport, or another Element.
8774         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8775         */
8776         center : function(centerIn){
8777             this.alignTo(centerIn || document, 'c-c');
8778             return this;
8779         },
8780
8781         /**
8782          * Tests various css rules/browsers to determine if this element uses a border box
8783          * @return {Boolean}
8784          */
8785         isBorderBox : function(){
8786             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8787         },
8788
8789         /**
8790          * Return a box {x, y, width, height} that can be used to set another elements
8791          * size/location to match this element.
8792          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8793          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8794          * @return {Object} box An object in the format {x, y, width, height}
8795          */
8796         getBox : function(contentBox, local){
8797             var xy;
8798             if(!local){
8799                 xy = this.getXY();
8800             }else{
8801                 var left = parseInt(this.getStyle("left"), 10) || 0;
8802                 var top = parseInt(this.getStyle("top"), 10) || 0;
8803                 xy = [left, top];
8804             }
8805             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8806             if(!contentBox){
8807                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8808             }else{
8809                 var l = this.getBorderWidth("l")+this.getPadding("l");
8810                 var r = this.getBorderWidth("r")+this.getPadding("r");
8811                 var t = this.getBorderWidth("t")+this.getPadding("t");
8812                 var b = this.getBorderWidth("b")+this.getPadding("b");
8813                 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)};
8814             }
8815             bx.right = bx.x + bx.width;
8816             bx.bottom = bx.y + bx.height;
8817             return bx;
8818         },
8819
8820         /**
8821          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8822          for more information about the sides.
8823          * @param {String} sides
8824          * @return {Number}
8825          */
8826         getFrameWidth : function(sides, onlyContentBox){
8827             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8828         },
8829
8830         /**
8831          * 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.
8832          * @param {Object} box The box to fill {x, y, width, height}
8833          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8834          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8835          * @return {Roo.Element} this
8836          */
8837         setBox : function(box, adjust, animate){
8838             var w = box.width, h = box.height;
8839             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8840                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8841                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8842             }
8843             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8844             return this;
8845         },
8846
8847         /**
8848          * Forces the browser to repaint this element
8849          * @return {Roo.Element} this
8850          */
8851          repaint : function(){
8852             var dom = this.dom;
8853             this.addClass("x-repaint");
8854             setTimeout(function(){
8855                 Roo.get(dom).removeClass("x-repaint");
8856             }, 1);
8857             return this;
8858         },
8859
8860         /**
8861          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8862          * then it returns the calculated width of the sides (see getPadding)
8863          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8864          * @return {Object/Number}
8865          */
8866         getMargins : function(side){
8867             if(!side){
8868                 return {
8869                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8870                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8871                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8872                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8873                 };
8874             }else{
8875                 return this.addStyles(side, El.margins);
8876              }
8877         },
8878
8879         // private
8880         addStyles : function(sides, styles){
8881             var val = 0, v, w;
8882             for(var i = 0, len = sides.length; i < len; i++){
8883                 v = this.getStyle(styles[sides.charAt(i)]);
8884                 if(v){
8885                      w = parseInt(v, 10);
8886                      if(w){ val += w; }
8887                 }
8888             }
8889             return val;
8890         },
8891
8892         /**
8893          * Creates a proxy element of this element
8894          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8895          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8896          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8897          * @return {Roo.Element} The new proxy element
8898          */
8899         createProxy : function(config, renderTo, matchBox){
8900             if(renderTo){
8901                 renderTo = Roo.getDom(renderTo);
8902             }else{
8903                 renderTo = document.body;
8904             }
8905             config = typeof config == "object" ?
8906                 config : {tag : "div", cls: config};
8907             var proxy = Roo.DomHelper.append(renderTo, config, true);
8908             if(matchBox){
8909                proxy.setBox(this.getBox());
8910             }
8911             return proxy;
8912         },
8913
8914         /**
8915          * Puts a mask over this element to disable user interaction. Requires core.css.
8916          * This method can only be applied to elements which accept child nodes.
8917          * @param {String} msg (optional) A message to display in the mask
8918          * @param {String} msgCls (optional) A css class to apply to the msg element
8919          * @return {Element} The mask  element
8920          */
8921         mask : function(msg, msgCls)
8922         {
8923             if(this.getStyle("position") == "static"){
8924                 this.setStyle("position", "relative");
8925             }
8926             if(!this._mask){
8927                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8928             }
8929             this.addClass("x-masked");
8930             this._mask.setDisplayed(true);
8931             
8932             // we wander
8933             var z = 0;
8934             var dom = this.dom
8935             while (dom && dom.style) {
8936                 if (!isNaN(parseInt(dom.style.zIndex))) {
8937                     z = Math.max(z, parseInt(dom.style.zIndex));
8938                 }
8939                 dom = dom.parentNode;
8940             }
8941             // if we are masking the body - then it hides everything..
8942             if (this.dom == document.body) {
8943                 z = 1000000;
8944                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8945                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8946             }
8947            
8948             if(typeof msg == 'string'){
8949                 if(!this._maskMsg){
8950                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8951                 }
8952                 var mm = this._maskMsg;
8953                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8954                 mm.dom.firstChild.innerHTML = msg;
8955                 mm.setDisplayed(true);
8956                 mm.center(this);
8957                 mm.setStyle('z-index', z + 102);
8958             }
8959             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8960                 this._mask.setHeight(this.getHeight());
8961             }
8962             this._mask.setStyle('z-index', z + 100);
8963             
8964             return this._mask;
8965         },
8966
8967         /**
8968          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8969          * it is cached for reuse.
8970          */
8971         unmask : function(removeEl){
8972             if(this._mask){
8973                 if(removeEl === true){
8974                     this._mask.remove();
8975                     delete this._mask;
8976                     if(this._maskMsg){
8977                         this._maskMsg.remove();
8978                         delete this._maskMsg;
8979                     }
8980                 }else{
8981                     this._mask.setDisplayed(false);
8982                     if(this._maskMsg){
8983                         this._maskMsg.setDisplayed(false);
8984                     }
8985                 }
8986             }
8987             this.removeClass("x-masked");
8988         },
8989
8990         /**
8991          * Returns true if this element is masked
8992          * @return {Boolean}
8993          */
8994         isMasked : function(){
8995             return this._mask && this._mask.isVisible();
8996         },
8997
8998         /**
8999          * Creates an iframe shim for this element to keep selects and other windowed objects from
9000          * showing through.
9001          * @return {Roo.Element} The new shim element
9002          */
9003         createShim : function(){
9004             var el = document.createElement('iframe');
9005             el.frameBorder = 'no';
9006             el.className = 'roo-shim';
9007             if(Roo.isIE && Roo.isSecure){
9008                 el.src = Roo.SSL_SECURE_URL;
9009             }
9010             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9011             shim.autoBoxAdjust = false;
9012             return shim;
9013         },
9014
9015         /**
9016          * Removes this element from the DOM and deletes it from the cache
9017          */
9018         remove : function(){
9019             if(this.dom.parentNode){
9020                 this.dom.parentNode.removeChild(this.dom);
9021             }
9022             delete El.cache[this.dom.id];
9023         },
9024
9025         /**
9026          * Sets up event handlers to add and remove a css class when the mouse is over this element
9027          * @param {String} className
9028          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9029          * mouseout events for children elements
9030          * @return {Roo.Element} this
9031          */
9032         addClassOnOver : function(className, preventFlicker){
9033             this.on("mouseover", function(){
9034                 Roo.fly(this, '_internal').addClass(className);
9035             }, this.dom);
9036             var removeFn = function(e){
9037                 if(preventFlicker !== true || !e.within(this, true)){
9038                     Roo.fly(this, '_internal').removeClass(className);
9039                 }
9040             };
9041             this.on("mouseout", removeFn, this.dom);
9042             return this;
9043         },
9044
9045         /**
9046          * Sets up event handlers to add and remove a css class when this element has the focus
9047          * @param {String} className
9048          * @return {Roo.Element} this
9049          */
9050         addClassOnFocus : function(className){
9051             this.on("focus", function(){
9052                 Roo.fly(this, '_internal').addClass(className);
9053             }, this.dom);
9054             this.on("blur", function(){
9055                 Roo.fly(this, '_internal').removeClass(className);
9056             }, this.dom);
9057             return this;
9058         },
9059         /**
9060          * 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)
9061          * @param {String} className
9062          * @return {Roo.Element} this
9063          */
9064         addClassOnClick : function(className){
9065             var dom = this.dom;
9066             this.on("mousedown", function(){
9067                 Roo.fly(dom, '_internal').addClass(className);
9068                 var d = Roo.get(document);
9069                 var fn = function(){
9070                     Roo.fly(dom, '_internal').removeClass(className);
9071                     d.removeListener("mouseup", fn);
9072                 };
9073                 d.on("mouseup", fn);
9074             });
9075             return this;
9076         },
9077
9078         /**
9079          * Stops the specified event from bubbling and optionally prevents the default action
9080          * @param {String} eventName
9081          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9082          * @return {Roo.Element} this
9083          */
9084         swallowEvent : function(eventName, preventDefault){
9085             var fn = function(e){
9086                 e.stopPropagation();
9087                 if(preventDefault){
9088                     e.preventDefault();
9089                 }
9090             };
9091             if(eventName instanceof Array){
9092                 for(var i = 0, len = eventName.length; i < len; i++){
9093                      this.on(eventName[i], fn);
9094                 }
9095                 return this;
9096             }
9097             this.on(eventName, fn);
9098             return this;
9099         },
9100
9101         /**
9102          * @private
9103          */
9104       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9105
9106         /**
9107          * Sizes this element to its parent element's dimensions performing
9108          * neccessary box adjustments.
9109          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9110          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9111          * @return {Roo.Element} this
9112          */
9113         fitToParent : function(monitorResize, targetParent) {
9114           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9115           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9116           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9117             return;
9118           }
9119           var p = Roo.get(targetParent || this.dom.parentNode);
9120           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9121           if (monitorResize === true) {
9122             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9123             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9124           }
9125           return this;
9126         },
9127
9128         /**
9129          * Gets the next sibling, skipping text nodes
9130          * @return {HTMLElement} The next sibling or null
9131          */
9132         getNextSibling : function(){
9133             var n = this.dom.nextSibling;
9134             while(n && n.nodeType != 1){
9135                 n = n.nextSibling;
9136             }
9137             return n;
9138         },
9139
9140         /**
9141          * Gets the previous sibling, skipping text nodes
9142          * @return {HTMLElement} The previous sibling or null
9143          */
9144         getPrevSibling : function(){
9145             var n = this.dom.previousSibling;
9146             while(n && n.nodeType != 1){
9147                 n = n.previousSibling;
9148             }
9149             return n;
9150         },
9151
9152
9153         /**
9154          * Appends the passed element(s) to this element
9155          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9156          * @return {Roo.Element} this
9157          */
9158         appendChild: function(el){
9159             el = Roo.get(el);
9160             el.appendTo(this);
9161             return this;
9162         },
9163
9164         /**
9165          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9166          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9167          * automatically generated with the specified attributes.
9168          * @param {HTMLElement} insertBefore (optional) a child element of this element
9169          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9170          * @return {Roo.Element} The new child element
9171          */
9172         createChild: function(config, insertBefore, returnDom){
9173             config = config || {tag:'div'};
9174             if(insertBefore){
9175                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9176             }
9177             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9178         },
9179
9180         /**
9181          * Appends this element to the passed element
9182          * @param {String/HTMLElement/Element} el The new parent element
9183          * @return {Roo.Element} this
9184          */
9185         appendTo: function(el){
9186             el = Roo.getDom(el);
9187             el.appendChild(this.dom);
9188             return this;
9189         },
9190
9191         /**
9192          * Inserts this element before the passed element in the DOM
9193          * @param {String/HTMLElement/Element} el The element to insert before
9194          * @return {Roo.Element} this
9195          */
9196         insertBefore: function(el){
9197             el = Roo.getDom(el);
9198             el.parentNode.insertBefore(this.dom, el);
9199             return this;
9200         },
9201
9202         /**
9203          * Inserts this element after the passed element in the DOM
9204          * @param {String/HTMLElement/Element} el The element to insert after
9205          * @return {Roo.Element} this
9206          */
9207         insertAfter: function(el){
9208             el = Roo.getDom(el);
9209             el.parentNode.insertBefore(this.dom, el.nextSibling);
9210             return this;
9211         },
9212
9213         /**
9214          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9215          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9216          * @return {Roo.Element} The new child
9217          */
9218         insertFirst: function(el, returnDom){
9219             el = el || {};
9220             if(typeof el == 'object' && !el.nodeType){ // dh config
9221                 return this.createChild(el, this.dom.firstChild, returnDom);
9222             }else{
9223                 el = Roo.getDom(el);
9224                 this.dom.insertBefore(el, this.dom.firstChild);
9225                 return !returnDom ? Roo.get(el) : el;
9226             }
9227         },
9228
9229         /**
9230          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9231          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9232          * @param {String} where (optional) 'before' or 'after' defaults to before
9233          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9234          * @return {Roo.Element} the inserted Element
9235          */
9236         insertSibling: function(el, where, returnDom){
9237             where = where ? where.toLowerCase() : 'before';
9238             el = el || {};
9239             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9240
9241             if(typeof el == 'object' && !el.nodeType){ // dh config
9242                 if(where == 'after' && !this.dom.nextSibling){
9243                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9244                 }else{
9245                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9246                 }
9247
9248             }else{
9249                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9250                             where == 'before' ? this.dom : this.dom.nextSibling);
9251                 if(!returnDom){
9252                     rt = Roo.get(rt);
9253                 }
9254             }
9255             return rt;
9256         },
9257
9258         /**
9259          * Creates and wraps this element with another element
9260          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9261          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9262          * @return {HTMLElement/Element} The newly created wrapper element
9263          */
9264         wrap: function(config, returnDom){
9265             if(!config){
9266                 config = {tag: "div"};
9267             }
9268             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9269             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9270             return newEl;
9271         },
9272
9273         /**
9274          * Replaces the passed element with this element
9275          * @param {String/HTMLElement/Element} el The element to replace
9276          * @return {Roo.Element} this
9277          */
9278         replace: function(el){
9279             el = Roo.get(el);
9280             this.insertBefore(el);
9281             el.remove();
9282             return this;
9283         },
9284
9285         /**
9286          * Inserts an html fragment into this element
9287          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9288          * @param {String} html The HTML fragment
9289          * @param {Boolean} returnEl True to return an Roo.Element
9290          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9291          */
9292         insertHtml : function(where, html, returnEl){
9293             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9294             return returnEl ? Roo.get(el) : el;
9295         },
9296
9297         /**
9298          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9299          * @param {Object} o The object with the attributes
9300          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9301          * @return {Roo.Element} this
9302          */
9303         set : function(o, useSet){
9304             var el = this.dom;
9305             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9306             for(var attr in o){
9307                 if(attr == "style" || typeof o[attr] == "function") continue;
9308                 if(attr=="cls"){
9309                     el.className = o["cls"];
9310                 }else{
9311                     if(useSet) el.setAttribute(attr, o[attr]);
9312                     else el[attr] = o[attr];
9313                 }
9314             }
9315             if(o.style){
9316                 Roo.DomHelper.applyStyles(el, o.style);
9317             }
9318             return this;
9319         },
9320
9321         /**
9322          * Convenience method for constructing a KeyMap
9323          * @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:
9324          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9325          * @param {Function} fn The function to call
9326          * @param {Object} scope (optional) The scope of the function
9327          * @return {Roo.KeyMap} The KeyMap created
9328          */
9329         addKeyListener : function(key, fn, scope){
9330             var config;
9331             if(typeof key != "object" || key instanceof Array){
9332                 config = {
9333                     key: key,
9334                     fn: fn,
9335                     scope: scope
9336                 };
9337             }else{
9338                 config = {
9339                     key : key.key,
9340                     shift : key.shift,
9341                     ctrl : key.ctrl,
9342                     alt : key.alt,
9343                     fn: fn,
9344                     scope: scope
9345                 };
9346             }
9347             return new Roo.KeyMap(this, config);
9348         },
9349
9350         /**
9351          * Creates a KeyMap for this element
9352          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9353          * @return {Roo.KeyMap} The KeyMap created
9354          */
9355         addKeyMap : function(config){
9356             return new Roo.KeyMap(this, config);
9357         },
9358
9359         /**
9360          * Returns true if this element is scrollable.
9361          * @return {Boolean}
9362          */
9363          isScrollable : function(){
9364             var dom = this.dom;
9365             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9366         },
9367
9368         /**
9369          * 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().
9370          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9371          * @param {Number} value The new scroll value
9372          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9373          * @return {Element} this
9374          */
9375
9376         scrollTo : function(side, value, animate){
9377             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9378             if(!animate || !A){
9379                 this.dom[prop] = value;
9380             }else{
9381                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9382                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9383             }
9384             return this;
9385         },
9386
9387         /**
9388          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9389          * within this element's scrollable range.
9390          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9391          * @param {Number} distance How far to scroll the element in pixels
9392          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9393          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9394          * was scrolled as far as it could go.
9395          */
9396          scroll : function(direction, distance, animate){
9397              if(!this.isScrollable()){
9398                  return;
9399              }
9400              var el = this.dom;
9401              var l = el.scrollLeft, t = el.scrollTop;
9402              var w = el.scrollWidth, h = el.scrollHeight;
9403              var cw = el.clientWidth, ch = el.clientHeight;
9404              direction = direction.toLowerCase();
9405              var scrolled = false;
9406              var a = this.preanim(arguments, 2);
9407              switch(direction){
9408                  case "l":
9409                  case "left":
9410                      if(w - l > cw){
9411                          var v = Math.min(l + distance, w-cw);
9412                          this.scrollTo("left", v, a);
9413                          scrolled = true;
9414                      }
9415                      break;
9416                 case "r":
9417                 case "right":
9418                      if(l > 0){
9419                          var v = Math.max(l - distance, 0);
9420                          this.scrollTo("left", v, a);
9421                          scrolled = true;
9422                      }
9423                      break;
9424                 case "t":
9425                 case "top":
9426                 case "up":
9427                      if(t > 0){
9428                          var v = Math.max(t - distance, 0);
9429                          this.scrollTo("top", v, a);
9430                          scrolled = true;
9431                      }
9432                      break;
9433                 case "b":
9434                 case "bottom":
9435                 case "down":
9436                      if(h - t > ch){
9437                          var v = Math.min(t + distance, h-ch);
9438                          this.scrollTo("top", v, a);
9439                          scrolled = true;
9440                      }
9441                      break;
9442              }
9443              return scrolled;
9444         },
9445
9446         /**
9447          * Translates the passed page coordinates into left/top css values for this element
9448          * @param {Number/Array} x The page x or an array containing [x, y]
9449          * @param {Number} y The page y
9450          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9451          */
9452         translatePoints : function(x, y){
9453             if(typeof x == 'object' || x instanceof Array){
9454                 y = x[1]; x = x[0];
9455             }
9456             var p = this.getStyle('position');
9457             var o = this.getXY();
9458
9459             var l = parseInt(this.getStyle('left'), 10);
9460             var t = parseInt(this.getStyle('top'), 10);
9461
9462             if(isNaN(l)){
9463                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9464             }
9465             if(isNaN(t)){
9466                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9467             }
9468
9469             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9470         },
9471
9472         /**
9473          * Returns the current scroll position of the element.
9474          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9475          */
9476         getScroll : function(){
9477             var d = this.dom, doc = document;
9478             if(d == doc || d == doc.body){
9479                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9480                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9481                 return {left: l, top: t};
9482             }else{
9483                 return {left: d.scrollLeft, top: d.scrollTop};
9484             }
9485         },
9486
9487         /**
9488          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9489          * are convert to standard 6 digit hex color.
9490          * @param {String} attr The css attribute
9491          * @param {String} defaultValue The default value to use when a valid color isn't found
9492          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9493          * YUI color anims.
9494          */
9495         getColor : function(attr, defaultValue, prefix){
9496             var v = this.getStyle(attr);
9497             if(!v || v == "transparent" || v == "inherit") {
9498                 return defaultValue;
9499             }
9500             var color = typeof prefix == "undefined" ? "#" : prefix;
9501             if(v.substr(0, 4) == "rgb("){
9502                 var rvs = v.slice(4, v.length -1).split(",");
9503                 for(var i = 0; i < 3; i++){
9504                     var h = parseInt(rvs[i]).toString(16);
9505                     if(h < 16){
9506                         h = "0" + h;
9507                     }
9508                     color += h;
9509                 }
9510             } else {
9511                 if(v.substr(0, 1) == "#"){
9512                     if(v.length == 4) {
9513                         for(var i = 1; i < 4; i++){
9514                             var c = v.charAt(i);
9515                             color +=  c + c;
9516                         }
9517                     }else if(v.length == 7){
9518                         color += v.substr(1);
9519                     }
9520                 }
9521             }
9522             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9523         },
9524
9525         /**
9526          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9527          * gradient background, rounded corners and a 4-way shadow.
9528          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9529          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9530          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9531          * @return {Roo.Element} this
9532          */
9533         boxWrap : function(cls){
9534             cls = cls || 'x-box';
9535             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9536             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9537             return el;
9538         },
9539
9540         /**
9541          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9542          * @param {String} namespace The namespace in which to look for the attribute
9543          * @param {String} name The attribute name
9544          * @return {String} The attribute value
9545          */
9546         getAttributeNS : Roo.isIE ? function(ns, name){
9547             var d = this.dom;
9548             var type = typeof d[ns+":"+name];
9549             if(type != 'undefined' && type != 'unknown'){
9550                 return d[ns+":"+name];
9551             }
9552             return d[name];
9553         } : function(ns, name){
9554             var d = this.dom;
9555             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9556         }
9557     };
9558
9559     var ep = El.prototype;
9560
9561     /**
9562      * Appends an event handler (Shorthand for addListener)
9563      * @param {String}   eventName     The type of event to append
9564      * @param {Function} fn        The method the event invokes
9565      * @param {Object} scope       (optional) The scope (this object) of the fn
9566      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9567      * @method
9568      */
9569     ep.on = ep.addListener;
9570         // backwards compat
9571     ep.mon = ep.addListener;
9572
9573     /**
9574      * Removes an event handler from this element (shorthand for removeListener)
9575      * @param {String} eventName the type of event to remove
9576      * @param {Function} fn the method the event invokes
9577      * @return {Roo.Element} this
9578      * @method
9579      */
9580     ep.un = ep.removeListener;
9581
9582     /**
9583      * true to automatically adjust width and height settings for box-model issues (default to true)
9584      */
9585     ep.autoBoxAdjust = true;
9586
9587     // private
9588     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9589
9590     // private
9591     El.addUnits = function(v, defaultUnit){
9592         if(v === "" || v == "auto"){
9593             return v;
9594         }
9595         if(v === undefined){
9596             return '';
9597         }
9598         if(typeof v == "number" || !El.unitPattern.test(v)){
9599             return v + (defaultUnit || 'px');
9600         }
9601         return v;
9602     };
9603
9604     // special markup used throughout Roo when box wrapping elements
9605     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>';
9606     /**
9607      * Visibility mode constant - Use visibility to hide element
9608      * @static
9609      * @type Number
9610      */
9611     El.VISIBILITY = 1;
9612     /**
9613      * Visibility mode constant - Use display to hide element
9614      * @static
9615      * @type Number
9616      */
9617     El.DISPLAY = 2;
9618
9619     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9620     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9621     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9622
9623
9624
9625     /**
9626      * @private
9627      */
9628     El.cache = {};
9629
9630     var docEl;
9631
9632     /**
9633      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9634      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9635      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9636      * @return {Element} The Element object
9637      * @static
9638      */
9639     El.get = function(el){
9640         var ex, elm, id;
9641         if(!el){ return null; }
9642         if(typeof el == "string"){ // element id
9643             if(!(elm = document.getElementById(el))){
9644                 return null;
9645             }
9646             if(ex = El.cache[el]){
9647                 ex.dom = elm;
9648             }else{
9649                 ex = El.cache[el] = new El(elm);
9650             }
9651             return ex;
9652         }else if(el.tagName){ // dom element
9653             if(!(id = el.id)){
9654                 id = Roo.id(el);
9655             }
9656             if(ex = El.cache[id]){
9657                 ex.dom = el;
9658             }else{
9659                 ex = El.cache[id] = new El(el);
9660             }
9661             return ex;
9662         }else if(el instanceof El){
9663             if(el != docEl){
9664                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9665                                                               // catch case where it hasn't been appended
9666                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9667             }
9668             return el;
9669         }else if(el.isComposite){
9670             return el;
9671         }else if(el instanceof Array){
9672             return El.select(el);
9673         }else if(el == document){
9674             // create a bogus element object representing the document object
9675             if(!docEl){
9676                 var f = function(){};
9677                 f.prototype = El.prototype;
9678                 docEl = new f();
9679                 docEl.dom = document;
9680             }
9681             return docEl;
9682         }
9683         return null;
9684     };
9685
9686     // private
9687     El.uncache = function(el){
9688         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9689             if(a[i]){
9690                 delete El.cache[a[i].id || a[i]];
9691             }
9692         }
9693     };
9694
9695     // private
9696     // Garbage collection - uncache elements/purge listeners on orphaned elements
9697     // so we don't hold a reference and cause the browser to retain them
9698     El.garbageCollect = function(){
9699         if(!Roo.enableGarbageCollector){
9700             clearInterval(El.collectorThread);
9701             return;
9702         }
9703         for(var eid in El.cache){
9704             var el = El.cache[eid], d = el.dom;
9705             // -------------------------------------------------------
9706             // Determining what is garbage:
9707             // -------------------------------------------------------
9708             // !d
9709             // dom node is null, definitely garbage
9710             // -------------------------------------------------------
9711             // !d.parentNode
9712             // no parentNode == direct orphan, definitely garbage
9713             // -------------------------------------------------------
9714             // !d.offsetParent && !document.getElementById(eid)
9715             // display none elements have no offsetParent so we will
9716             // also try to look it up by it's id. However, check
9717             // offsetParent first so we don't do unneeded lookups.
9718             // This enables collection of elements that are not orphans
9719             // directly, but somewhere up the line they have an orphan
9720             // parent.
9721             // -------------------------------------------------------
9722             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9723                 delete El.cache[eid];
9724                 if(d && Roo.enableListenerCollection){
9725                     E.purgeElement(d);
9726                 }
9727             }
9728         }
9729     }
9730     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9731
9732
9733     // dom is optional
9734     El.Flyweight = function(dom){
9735         this.dom = dom;
9736     };
9737     El.Flyweight.prototype = El.prototype;
9738
9739     El._flyweights = {};
9740     /**
9741      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9742      * the dom node can be overwritten by other code.
9743      * @param {String/HTMLElement} el The dom node or id
9744      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9745      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9746      * @static
9747      * @return {Element} The shared Element object
9748      */
9749     El.fly = function(el, named){
9750         named = named || '_global';
9751         el = Roo.getDom(el);
9752         if(!el){
9753             return null;
9754         }
9755         if(!El._flyweights[named]){
9756             El._flyweights[named] = new El.Flyweight();
9757         }
9758         El._flyweights[named].dom = el;
9759         return El._flyweights[named];
9760     };
9761
9762     /**
9763      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9764      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9765      * Shorthand of {@link Roo.Element#get}
9766      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9767      * @return {Element} The Element object
9768      * @member Roo
9769      * @method get
9770      */
9771     Roo.get = El.get;
9772     /**
9773      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9774      * the dom node can be overwritten by other code.
9775      * Shorthand of {@link Roo.Element#fly}
9776      * @param {String/HTMLElement} el The dom node or id
9777      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9778      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9779      * @static
9780      * @return {Element} The shared Element object
9781      * @member Roo
9782      * @method fly
9783      */
9784     Roo.fly = El.fly;
9785
9786     // speedy lookup for elements never to box adjust
9787     var noBoxAdjust = Roo.isStrict ? {
9788         select:1
9789     } : {
9790         input:1, select:1, textarea:1
9791     };
9792     if(Roo.isIE || Roo.isGecko){
9793         noBoxAdjust['button'] = 1;
9794     }
9795
9796
9797     Roo.EventManager.on(window, 'unload', function(){
9798         delete El.cache;
9799         delete El._flyweights;
9800     });
9801 })();
9802
9803
9804
9805
9806 if(Roo.DomQuery){
9807     Roo.Element.selectorFunction = Roo.DomQuery.select;
9808 }
9809
9810 Roo.Element.select = function(selector, unique, root){
9811     var els;
9812     if(typeof selector == "string"){
9813         els = Roo.Element.selectorFunction(selector, root);
9814     }else if(selector.length !== undefined){
9815         els = selector;
9816     }else{
9817         throw "Invalid selector";
9818     }
9819     if(unique === true){
9820         return new Roo.CompositeElement(els);
9821     }else{
9822         return new Roo.CompositeElementLite(els);
9823     }
9824 };
9825 /**
9826  * Selects elements based on the passed CSS selector to enable working on them as 1.
9827  * @param {String/Array} selector The CSS selector or an array of elements
9828  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9829  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9830  * @return {CompositeElementLite/CompositeElement}
9831  * @member Roo
9832  * @method select
9833  */
9834 Roo.select = Roo.Element.select;
9835
9836
9837
9838
9839
9840
9841
9842
9843
9844
9845
9846
9847
9848
9849 /*
9850  * Based on:
9851  * Ext JS Library 1.1.1
9852  * Copyright(c) 2006-2007, Ext JS, LLC.
9853  *
9854  * Originally Released Under LGPL - original licence link has changed is not relivant.
9855  *
9856  * Fork - LGPL
9857  * <script type="text/javascript">
9858  */
9859
9860
9861
9862 //Notifies Element that fx methods are available
9863 Roo.enableFx = true;
9864
9865 /**
9866  * @class Roo.Fx
9867  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9868  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9869  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9870  * Element effects to work.</p><br/>
9871  *
9872  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9873  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9874  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9875  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9876  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9877  * expected results and should be done with care.</p><br/>
9878  *
9879  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9880  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9881 <pre>
9882 Value  Description
9883 -----  -----------------------------
9884 tl     The top left corner
9885 t      The center of the top edge
9886 tr     The top right corner
9887 l      The center of the left edge
9888 r      The center of the right edge
9889 bl     The bottom left corner
9890 b      The center of the bottom edge
9891 br     The bottom right corner
9892 </pre>
9893  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9894  * below are common options that can be passed to any Fx method.</b>
9895  * @cfg {Function} callback A function called when the effect is finished
9896  * @cfg {Object} scope The scope of the effect function
9897  * @cfg {String} easing A valid Easing value for the effect
9898  * @cfg {String} afterCls A css class to apply after the effect
9899  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9900  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9901  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9902  * effects that end with the element being visually hidden, ignored otherwise)
9903  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9904  * a function which returns such a specification that will be applied to the Element after the effect finishes
9905  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9906  * @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
9907  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9908  */
9909 Roo.Fx = {
9910         /**
9911          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9912          * origin for the slide effect.  This function automatically handles wrapping the element with
9913          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9914          * Usage:
9915          *<pre><code>
9916 // default: slide the element in from the top
9917 el.slideIn();
9918
9919 // custom: slide the element in from the right with a 2-second duration
9920 el.slideIn('r', { duration: 2 });
9921
9922 // common config options shown with default values
9923 el.slideIn('t', {
9924     easing: 'easeOut',
9925     duration: .5
9926 });
9927 </code></pre>
9928          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9929          * @param {Object} options (optional) Object literal with any of the Fx config options
9930          * @return {Roo.Element} The Element
9931          */
9932     slideIn : function(anchor, o){
9933         var el = this.getFxEl();
9934         o = o || {};
9935
9936         el.queueFx(o, function(){
9937
9938             anchor = anchor || "t";
9939
9940             // fix display to visibility
9941             this.fixDisplay();
9942
9943             // restore values after effect
9944             var r = this.getFxRestore();
9945             var b = this.getBox();
9946             // fixed size for slide
9947             this.setSize(b);
9948
9949             // wrap if needed
9950             var wrap = this.fxWrap(r.pos, o, "hidden");
9951
9952             var st = this.dom.style;
9953             st.visibility = "visible";
9954             st.position = "absolute";
9955
9956             // clear out temp styles after slide and unwrap
9957             var after = function(){
9958                 el.fxUnwrap(wrap, r.pos, o);
9959                 st.width = r.width;
9960                 st.height = r.height;
9961                 el.afterFx(o);
9962             };
9963             // time to calc the positions
9964             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9965
9966             switch(anchor.toLowerCase()){
9967                 case "t":
9968                     wrap.setSize(b.width, 0);
9969                     st.left = st.bottom = "0";
9970                     a = {height: bh};
9971                 break;
9972                 case "l":
9973                     wrap.setSize(0, b.height);
9974                     st.right = st.top = "0";
9975                     a = {width: bw};
9976                 break;
9977                 case "r":
9978                     wrap.setSize(0, b.height);
9979                     wrap.setX(b.right);
9980                     st.left = st.top = "0";
9981                     a = {width: bw, points: pt};
9982                 break;
9983                 case "b":
9984                     wrap.setSize(b.width, 0);
9985                     wrap.setY(b.bottom);
9986                     st.left = st.top = "0";
9987                     a = {height: bh, points: pt};
9988                 break;
9989                 case "tl":
9990                     wrap.setSize(0, 0);
9991                     st.right = st.bottom = "0";
9992                     a = {width: bw, height: bh};
9993                 break;
9994                 case "bl":
9995                     wrap.setSize(0, 0);
9996                     wrap.setY(b.y+b.height);
9997                     st.right = st.top = "0";
9998                     a = {width: bw, height: bh, points: pt};
9999                 break;
10000                 case "br":
10001                     wrap.setSize(0, 0);
10002                     wrap.setXY([b.right, b.bottom]);
10003                     st.left = st.top = "0";
10004                     a = {width: bw, height: bh, points: pt};
10005                 break;
10006                 case "tr":
10007                     wrap.setSize(0, 0);
10008                     wrap.setX(b.x+b.width);
10009                     st.left = st.bottom = "0";
10010                     a = {width: bw, height: bh, points: pt};
10011                 break;
10012             }
10013             this.dom.style.visibility = "visible";
10014             wrap.show();
10015
10016             arguments.callee.anim = wrap.fxanim(a,
10017                 o,
10018                 'motion',
10019                 .5,
10020                 'easeOut', after);
10021         });
10022         return this;
10023     },
10024     
10025         /**
10026          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10027          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10028          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10029          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10030          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10031          * Usage:
10032          *<pre><code>
10033 // default: slide the element out to the top
10034 el.slideOut();
10035
10036 // custom: slide the element out to the right with a 2-second duration
10037 el.slideOut('r', { duration: 2 });
10038
10039 // common config options shown with default values
10040 el.slideOut('t', {
10041     easing: 'easeOut',
10042     duration: .5,
10043     remove: false,
10044     useDisplay: false
10045 });
10046 </code></pre>
10047          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10048          * @param {Object} options (optional) Object literal with any of the Fx config options
10049          * @return {Roo.Element} The Element
10050          */
10051     slideOut : function(anchor, o){
10052         var el = this.getFxEl();
10053         o = o || {};
10054
10055         el.queueFx(o, function(){
10056
10057             anchor = anchor || "t";
10058
10059             // restore values after effect
10060             var r = this.getFxRestore();
10061             
10062             var b = this.getBox();
10063             // fixed size for slide
10064             this.setSize(b);
10065
10066             // wrap if needed
10067             var wrap = this.fxWrap(r.pos, o, "visible");
10068
10069             var st = this.dom.style;
10070             st.visibility = "visible";
10071             st.position = "absolute";
10072
10073             wrap.setSize(b);
10074
10075             var after = function(){
10076                 if(o.useDisplay){
10077                     el.setDisplayed(false);
10078                 }else{
10079                     el.hide();
10080                 }
10081
10082                 el.fxUnwrap(wrap, r.pos, o);
10083
10084                 st.width = r.width;
10085                 st.height = r.height;
10086
10087                 el.afterFx(o);
10088             };
10089
10090             var a, zero = {to: 0};
10091             switch(anchor.toLowerCase()){
10092                 case "t":
10093                     st.left = st.bottom = "0";
10094                     a = {height: zero};
10095                 break;
10096                 case "l":
10097                     st.right = st.top = "0";
10098                     a = {width: zero};
10099                 break;
10100                 case "r":
10101                     st.left = st.top = "0";
10102                     a = {width: zero, points: {to:[b.right, b.y]}};
10103                 break;
10104                 case "b":
10105                     st.left = st.top = "0";
10106                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10107                 break;
10108                 case "tl":
10109                     st.right = st.bottom = "0";
10110                     a = {width: zero, height: zero};
10111                 break;
10112                 case "bl":
10113                     st.right = st.top = "0";
10114                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10115                 break;
10116                 case "br":
10117                     st.left = st.top = "0";
10118                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10119                 break;
10120                 case "tr":
10121                     st.left = st.bottom = "0";
10122                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10123                 break;
10124             }
10125
10126             arguments.callee.anim = wrap.fxanim(a,
10127                 o,
10128                 'motion',
10129                 .5,
10130                 "easeOut", after);
10131         });
10132         return this;
10133     },
10134
10135         /**
10136          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10137          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10138          * The element must be removed from the DOM using the 'remove' config option if desired.
10139          * Usage:
10140          *<pre><code>
10141 // default
10142 el.puff();
10143
10144 // common config options shown with default values
10145 el.puff({
10146     easing: 'easeOut',
10147     duration: .5,
10148     remove: false,
10149     useDisplay: false
10150 });
10151 </code></pre>
10152          * @param {Object} options (optional) Object literal with any of the Fx config options
10153          * @return {Roo.Element} The Element
10154          */
10155     puff : function(o){
10156         var el = this.getFxEl();
10157         o = o || {};
10158
10159         el.queueFx(o, function(){
10160             this.clearOpacity();
10161             this.show();
10162
10163             // restore values after effect
10164             var r = this.getFxRestore();
10165             var st = this.dom.style;
10166
10167             var after = function(){
10168                 if(o.useDisplay){
10169                     el.setDisplayed(false);
10170                 }else{
10171                     el.hide();
10172                 }
10173
10174                 el.clearOpacity();
10175
10176                 el.setPositioning(r.pos);
10177                 st.width = r.width;
10178                 st.height = r.height;
10179                 st.fontSize = '';
10180                 el.afterFx(o);
10181             };
10182
10183             var width = this.getWidth();
10184             var height = this.getHeight();
10185
10186             arguments.callee.anim = this.fxanim({
10187                     width : {to: this.adjustWidth(width * 2)},
10188                     height : {to: this.adjustHeight(height * 2)},
10189                     points : {by: [-(width * .5), -(height * .5)]},
10190                     opacity : {to: 0},
10191                     fontSize: {to:200, unit: "%"}
10192                 },
10193                 o,
10194                 'motion',
10195                 .5,
10196                 "easeOut", after);
10197         });
10198         return this;
10199     },
10200
10201         /**
10202          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10203          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10204          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10205          * Usage:
10206          *<pre><code>
10207 // default
10208 el.switchOff();
10209
10210 // all config options shown with default values
10211 el.switchOff({
10212     easing: 'easeIn',
10213     duration: .3,
10214     remove: false,
10215     useDisplay: false
10216 });
10217 </code></pre>
10218          * @param {Object} options (optional) Object literal with any of the Fx config options
10219          * @return {Roo.Element} The Element
10220          */
10221     switchOff : function(o){
10222         var el = this.getFxEl();
10223         o = o || {};
10224
10225         el.queueFx(o, function(){
10226             this.clearOpacity();
10227             this.clip();
10228
10229             // restore values after effect
10230             var r = this.getFxRestore();
10231             var st = this.dom.style;
10232
10233             var after = function(){
10234                 if(o.useDisplay){
10235                     el.setDisplayed(false);
10236                 }else{
10237                     el.hide();
10238                 }
10239
10240                 el.clearOpacity();
10241                 el.setPositioning(r.pos);
10242                 st.width = r.width;
10243                 st.height = r.height;
10244
10245                 el.afterFx(o);
10246             };
10247
10248             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10249                 this.clearOpacity();
10250                 (function(){
10251                     this.fxanim({
10252                         height:{to:1},
10253                         points:{by:[0, this.getHeight() * .5]}
10254                     }, o, 'motion', 0.3, 'easeIn', after);
10255                 }).defer(100, this);
10256             });
10257         });
10258         return this;
10259     },
10260
10261     /**
10262      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10263      * changed using the "attr" config option) and then fading back to the original color. If no original
10264      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10265      * Usage:
10266 <pre><code>
10267 // default: highlight background to yellow
10268 el.highlight();
10269
10270 // custom: highlight foreground text to blue for 2 seconds
10271 el.highlight("0000ff", { attr: 'color', duration: 2 });
10272
10273 // common config options shown with default values
10274 el.highlight("ffff9c", {
10275     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10276     endColor: (current color) or "ffffff",
10277     easing: 'easeIn',
10278     duration: 1
10279 });
10280 </code></pre>
10281      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10282      * @param {Object} options (optional) Object literal with any of the Fx config options
10283      * @return {Roo.Element} The Element
10284      */ 
10285     highlight : function(color, o){
10286         var el = this.getFxEl();
10287         o = o || {};
10288
10289         el.queueFx(o, function(){
10290             color = color || "ffff9c";
10291             attr = o.attr || "backgroundColor";
10292
10293             this.clearOpacity();
10294             this.show();
10295
10296             var origColor = this.getColor(attr);
10297             var restoreColor = this.dom.style[attr];
10298             endColor = (o.endColor || origColor) || "ffffff";
10299
10300             var after = function(){
10301                 el.dom.style[attr] = restoreColor;
10302                 el.afterFx(o);
10303             };
10304
10305             var a = {};
10306             a[attr] = {from: color, to: endColor};
10307             arguments.callee.anim = this.fxanim(a,
10308                 o,
10309                 'color',
10310                 1,
10311                 'easeIn', after);
10312         });
10313         return this;
10314     },
10315
10316    /**
10317     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10318     * Usage:
10319 <pre><code>
10320 // default: a single light blue ripple
10321 el.frame();
10322
10323 // custom: 3 red ripples lasting 3 seconds total
10324 el.frame("ff0000", 3, { duration: 3 });
10325
10326 // common config options shown with default values
10327 el.frame("C3DAF9", 1, {
10328     duration: 1 //duration of entire animation (not each individual ripple)
10329     // Note: Easing is not configurable and will be ignored if included
10330 });
10331 </code></pre>
10332     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10333     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10334     * @param {Object} options (optional) Object literal with any of the Fx config options
10335     * @return {Roo.Element} The Element
10336     */
10337     frame : function(color, count, o){
10338         var el = this.getFxEl();
10339         o = o || {};
10340
10341         el.queueFx(o, function(){
10342             color = color || "#C3DAF9";
10343             if(color.length == 6){
10344                 color = "#" + color;
10345             }
10346             count = count || 1;
10347             duration = o.duration || 1;
10348             this.show();
10349
10350             var b = this.getBox();
10351             var animFn = function(){
10352                 var proxy = this.createProxy({
10353
10354                      style:{
10355                         visbility:"hidden",
10356                         position:"absolute",
10357                         "z-index":"35000", // yee haw
10358                         border:"0px solid " + color
10359                      }
10360                   });
10361                 var scale = Roo.isBorderBox ? 2 : 1;
10362                 proxy.animate({
10363                     top:{from:b.y, to:b.y - 20},
10364                     left:{from:b.x, to:b.x - 20},
10365                     borderWidth:{from:0, to:10},
10366                     opacity:{from:1, to:0},
10367                     height:{from:b.height, to:(b.height + (20*scale))},
10368                     width:{from:b.width, to:(b.width + (20*scale))}
10369                 }, duration, function(){
10370                     proxy.remove();
10371                 });
10372                 if(--count > 0){
10373                      animFn.defer((duration/2)*1000, this);
10374                 }else{
10375                     el.afterFx(o);
10376                 }
10377             };
10378             animFn.call(this);
10379         });
10380         return this;
10381     },
10382
10383    /**
10384     * Creates a pause before any subsequent queued effects begin.  If there are
10385     * no effects queued after the pause it will have no effect.
10386     * Usage:
10387 <pre><code>
10388 el.pause(1);
10389 </code></pre>
10390     * @param {Number} seconds The length of time to pause (in seconds)
10391     * @return {Roo.Element} The Element
10392     */
10393     pause : function(seconds){
10394         var el = this.getFxEl();
10395         var o = {};
10396
10397         el.queueFx(o, function(){
10398             setTimeout(function(){
10399                 el.afterFx(o);
10400             }, seconds * 1000);
10401         });
10402         return this;
10403     },
10404
10405    /**
10406     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10407     * using the "endOpacity" config option.
10408     * Usage:
10409 <pre><code>
10410 // default: fade in from opacity 0 to 100%
10411 el.fadeIn();
10412
10413 // custom: fade in from opacity 0 to 75% over 2 seconds
10414 el.fadeIn({ endOpacity: .75, duration: 2});
10415
10416 // common config options shown with default values
10417 el.fadeIn({
10418     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10419     easing: 'easeOut',
10420     duration: .5
10421 });
10422 </code></pre>
10423     * @param {Object} options (optional) Object literal with any of the Fx config options
10424     * @return {Roo.Element} The Element
10425     */
10426     fadeIn : function(o){
10427         var el = this.getFxEl();
10428         o = o || {};
10429         el.queueFx(o, function(){
10430             this.setOpacity(0);
10431             this.fixDisplay();
10432             this.dom.style.visibility = 'visible';
10433             var to = o.endOpacity || 1;
10434             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10435                 o, null, .5, "easeOut", function(){
10436                 if(to == 1){
10437                     this.clearOpacity();
10438                 }
10439                 el.afterFx(o);
10440             });
10441         });
10442         return this;
10443     },
10444
10445    /**
10446     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10447     * using the "endOpacity" config option.
10448     * Usage:
10449 <pre><code>
10450 // default: fade out from the element's current opacity to 0
10451 el.fadeOut();
10452
10453 // custom: fade out from the element's current opacity to 25% over 2 seconds
10454 el.fadeOut({ endOpacity: .25, duration: 2});
10455
10456 // common config options shown with default values
10457 el.fadeOut({
10458     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10459     easing: 'easeOut',
10460     duration: .5
10461     remove: false,
10462     useDisplay: false
10463 });
10464 </code></pre>
10465     * @param {Object} options (optional) Object literal with any of the Fx config options
10466     * @return {Roo.Element} The Element
10467     */
10468     fadeOut : function(o){
10469         var el = this.getFxEl();
10470         o = o || {};
10471         el.queueFx(o, function(){
10472             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10473                 o, null, .5, "easeOut", function(){
10474                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10475                      this.dom.style.display = "none";
10476                 }else{
10477                      this.dom.style.visibility = "hidden";
10478                 }
10479                 this.clearOpacity();
10480                 el.afterFx(o);
10481             });
10482         });
10483         return this;
10484     },
10485
10486    /**
10487     * Animates the transition of an element's dimensions from a starting height/width
10488     * to an ending height/width.
10489     * Usage:
10490 <pre><code>
10491 // change height and width to 100x100 pixels
10492 el.scale(100, 100);
10493
10494 // common config options shown with default values.  The height and width will default to
10495 // the element's existing values if passed as null.
10496 el.scale(
10497     [element's width],
10498     [element's height], {
10499     easing: 'easeOut',
10500     duration: .35
10501 });
10502 </code></pre>
10503     * @param {Number} width  The new width (pass undefined to keep the original width)
10504     * @param {Number} height  The new height (pass undefined to keep the original height)
10505     * @param {Object} options (optional) Object literal with any of the Fx config options
10506     * @return {Roo.Element} The Element
10507     */
10508     scale : function(w, h, o){
10509         this.shift(Roo.apply({}, o, {
10510             width: w,
10511             height: h
10512         }));
10513         return this;
10514     },
10515
10516    /**
10517     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10518     * Any of these properties not specified in the config object will not be changed.  This effect 
10519     * requires that at least one new dimension, position or opacity setting must be passed in on
10520     * the config object in order for the function to have any effect.
10521     * Usage:
10522 <pre><code>
10523 // slide the element horizontally to x position 200 while changing the height and opacity
10524 el.shift({ x: 200, height: 50, opacity: .8 });
10525
10526 // common config options shown with default values.
10527 el.shift({
10528     width: [element's width],
10529     height: [element's height],
10530     x: [element's x position],
10531     y: [element's y position],
10532     opacity: [element's opacity],
10533     easing: 'easeOut',
10534     duration: .35
10535 });
10536 </code></pre>
10537     * @param {Object} options  Object literal with any of the Fx config options
10538     * @return {Roo.Element} The Element
10539     */
10540     shift : function(o){
10541         var el = this.getFxEl();
10542         o = o || {};
10543         el.queueFx(o, function(){
10544             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10545             if(w !== undefined){
10546                 a.width = {to: this.adjustWidth(w)};
10547             }
10548             if(h !== undefined){
10549                 a.height = {to: this.adjustHeight(h)};
10550             }
10551             if(x !== undefined || y !== undefined){
10552                 a.points = {to: [
10553                     x !== undefined ? x : this.getX(),
10554                     y !== undefined ? y : this.getY()
10555                 ]};
10556             }
10557             if(op !== undefined){
10558                 a.opacity = {to: op};
10559             }
10560             if(o.xy !== undefined){
10561                 a.points = {to: o.xy};
10562             }
10563             arguments.callee.anim = this.fxanim(a,
10564                 o, 'motion', .35, "easeOut", function(){
10565                 el.afterFx(o);
10566             });
10567         });
10568         return this;
10569     },
10570
10571         /**
10572          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10573          * ending point of the effect.
10574          * Usage:
10575          *<pre><code>
10576 // default: slide the element downward while fading out
10577 el.ghost();
10578
10579 // custom: slide the element out to the right with a 2-second duration
10580 el.ghost('r', { duration: 2 });
10581
10582 // common config options shown with default values
10583 el.ghost('b', {
10584     easing: 'easeOut',
10585     duration: .5
10586     remove: false,
10587     useDisplay: false
10588 });
10589 </code></pre>
10590          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10591          * @param {Object} options (optional) Object literal with any of the Fx config options
10592          * @return {Roo.Element} The Element
10593          */
10594     ghost : function(anchor, o){
10595         var el = this.getFxEl();
10596         o = o || {};
10597
10598         el.queueFx(o, function(){
10599             anchor = anchor || "b";
10600
10601             // restore values after effect
10602             var r = this.getFxRestore();
10603             var w = this.getWidth(),
10604                 h = this.getHeight();
10605
10606             var st = this.dom.style;
10607
10608             var after = function(){
10609                 if(o.useDisplay){
10610                     el.setDisplayed(false);
10611                 }else{
10612                     el.hide();
10613                 }
10614
10615                 el.clearOpacity();
10616                 el.setPositioning(r.pos);
10617                 st.width = r.width;
10618                 st.height = r.height;
10619
10620                 el.afterFx(o);
10621             };
10622
10623             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10624             switch(anchor.toLowerCase()){
10625                 case "t":
10626                     pt.by = [0, -h];
10627                 break;
10628                 case "l":
10629                     pt.by = [-w, 0];
10630                 break;
10631                 case "r":
10632                     pt.by = [w, 0];
10633                 break;
10634                 case "b":
10635                     pt.by = [0, h];
10636                 break;
10637                 case "tl":
10638                     pt.by = [-w, -h];
10639                 break;
10640                 case "bl":
10641                     pt.by = [-w, h];
10642                 break;
10643                 case "br":
10644                     pt.by = [w, h];
10645                 break;
10646                 case "tr":
10647                     pt.by = [w, -h];
10648                 break;
10649             }
10650
10651             arguments.callee.anim = this.fxanim(a,
10652                 o,
10653                 'motion',
10654                 .5,
10655                 "easeOut", after);
10656         });
10657         return this;
10658     },
10659
10660         /**
10661          * Ensures that all effects queued after syncFx is called on the element are
10662          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10663          * @return {Roo.Element} The Element
10664          */
10665     syncFx : function(){
10666         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10667             block : false,
10668             concurrent : true,
10669             stopFx : false
10670         });
10671         return this;
10672     },
10673
10674         /**
10675          * Ensures that all effects queued after sequenceFx is called on the element are
10676          * run in sequence.  This is the opposite of {@link #syncFx}.
10677          * @return {Roo.Element} The Element
10678          */
10679     sequenceFx : function(){
10680         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10681             block : false,
10682             concurrent : false,
10683             stopFx : false
10684         });
10685         return this;
10686     },
10687
10688         /* @private */
10689     nextFx : function(){
10690         var ef = this.fxQueue[0];
10691         if(ef){
10692             ef.call(this);
10693         }
10694     },
10695
10696         /**
10697          * Returns true if the element has any effects actively running or queued, else returns false.
10698          * @return {Boolean} True if element has active effects, else false
10699          */
10700     hasActiveFx : function(){
10701         return this.fxQueue && this.fxQueue[0];
10702     },
10703
10704         /**
10705          * Stops any running effects and clears the element's internal effects queue if it contains
10706          * any additional effects that haven't started yet.
10707          * @return {Roo.Element} The Element
10708          */
10709     stopFx : function(){
10710         if(this.hasActiveFx()){
10711             var cur = this.fxQueue[0];
10712             if(cur && cur.anim && cur.anim.isAnimated()){
10713                 this.fxQueue = [cur]; // clear out others
10714                 cur.anim.stop(true);
10715             }
10716         }
10717         return this;
10718     },
10719
10720         /* @private */
10721     beforeFx : function(o){
10722         if(this.hasActiveFx() && !o.concurrent){
10723            if(o.stopFx){
10724                this.stopFx();
10725                return true;
10726            }
10727            return false;
10728         }
10729         return true;
10730     },
10731
10732         /**
10733          * Returns true if the element is currently blocking so that no other effect can be queued
10734          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10735          * used to ensure that an effect initiated by a user action runs to completion prior to the
10736          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10737          * @return {Boolean} True if blocking, else false
10738          */
10739     hasFxBlock : function(){
10740         var q = this.fxQueue;
10741         return q && q[0] && q[0].block;
10742     },
10743
10744         /* @private */
10745     queueFx : function(o, fn){
10746         if(!this.fxQueue){
10747             this.fxQueue = [];
10748         }
10749         if(!this.hasFxBlock()){
10750             Roo.applyIf(o, this.fxDefaults);
10751             if(!o.concurrent){
10752                 var run = this.beforeFx(o);
10753                 fn.block = o.block;
10754                 this.fxQueue.push(fn);
10755                 if(run){
10756                     this.nextFx();
10757                 }
10758             }else{
10759                 fn.call(this);
10760             }
10761         }
10762         return this;
10763     },
10764
10765         /* @private */
10766     fxWrap : function(pos, o, vis){
10767         var wrap;
10768         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10769             var wrapXY;
10770             if(o.fixPosition){
10771                 wrapXY = this.getXY();
10772             }
10773             var div = document.createElement("div");
10774             div.style.visibility = vis;
10775             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10776             wrap.setPositioning(pos);
10777             if(wrap.getStyle("position") == "static"){
10778                 wrap.position("relative");
10779             }
10780             this.clearPositioning('auto');
10781             wrap.clip();
10782             wrap.dom.appendChild(this.dom);
10783             if(wrapXY){
10784                 wrap.setXY(wrapXY);
10785             }
10786         }
10787         return wrap;
10788     },
10789
10790         /* @private */
10791     fxUnwrap : function(wrap, pos, o){
10792         this.clearPositioning();
10793         this.setPositioning(pos);
10794         if(!o.wrap){
10795             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10796             wrap.remove();
10797         }
10798     },
10799
10800         /* @private */
10801     getFxRestore : function(){
10802         var st = this.dom.style;
10803         return {pos: this.getPositioning(), width: st.width, height : st.height};
10804     },
10805
10806         /* @private */
10807     afterFx : function(o){
10808         if(o.afterStyle){
10809             this.applyStyles(o.afterStyle);
10810         }
10811         if(o.afterCls){
10812             this.addClass(o.afterCls);
10813         }
10814         if(o.remove === true){
10815             this.remove();
10816         }
10817         Roo.callback(o.callback, o.scope, [this]);
10818         if(!o.concurrent){
10819             this.fxQueue.shift();
10820             this.nextFx();
10821         }
10822     },
10823
10824         /* @private */
10825     getFxEl : function(){ // support for composite element fx
10826         return Roo.get(this.dom);
10827     },
10828
10829         /* @private */
10830     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10831         animType = animType || 'run';
10832         opt = opt || {};
10833         var anim = Roo.lib.Anim[animType](
10834             this.dom, args,
10835             (opt.duration || defaultDur) || .35,
10836             (opt.easing || defaultEase) || 'easeOut',
10837             function(){
10838                 Roo.callback(cb, this);
10839             },
10840             this
10841         );
10842         opt.anim = anim;
10843         return anim;
10844     }
10845 };
10846
10847 // backwords compat
10848 Roo.Fx.resize = Roo.Fx.scale;
10849
10850 //When included, Roo.Fx is automatically applied to Element so that all basic
10851 //effects are available directly via the Element API
10852 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10853  * Based on:
10854  * Ext JS Library 1.1.1
10855  * Copyright(c) 2006-2007, Ext JS, LLC.
10856  *
10857  * Originally Released Under LGPL - original licence link has changed is not relivant.
10858  *
10859  * Fork - LGPL
10860  * <script type="text/javascript">
10861  */
10862
10863
10864 /**
10865  * @class Roo.CompositeElement
10866  * Standard composite class. Creates a Roo.Element for every element in the collection.
10867  * <br><br>
10868  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10869  * actions will be performed on all the elements in this collection.</b>
10870  * <br><br>
10871  * All methods return <i>this</i> and can be chained.
10872  <pre><code>
10873  var els = Roo.select("#some-el div.some-class", true);
10874  // or select directly from an existing element
10875  var el = Roo.get('some-el');
10876  el.select('div.some-class', true);
10877
10878  els.setWidth(100); // all elements become 100 width
10879  els.hide(true); // all elements fade out and hide
10880  // or
10881  els.setWidth(100).hide(true);
10882  </code></pre>
10883  */
10884 Roo.CompositeElement = function(els){
10885     this.elements = [];
10886     this.addElements(els);
10887 };
10888 Roo.CompositeElement.prototype = {
10889     isComposite: true,
10890     addElements : function(els){
10891         if(!els) return this;
10892         if(typeof els == "string"){
10893             els = Roo.Element.selectorFunction(els);
10894         }
10895         var yels = this.elements;
10896         var index = yels.length-1;
10897         for(var i = 0, len = els.length; i < len; i++) {
10898                 yels[++index] = Roo.get(els[i]);
10899         }
10900         return this;
10901     },
10902
10903     /**
10904     * Clears this composite and adds the elements returned by the passed selector.
10905     * @param {String/Array} els A string CSS selector, an array of elements or an element
10906     * @return {CompositeElement} this
10907     */
10908     fill : function(els){
10909         this.elements = [];
10910         this.add(els);
10911         return this;
10912     },
10913
10914     /**
10915     * Filters this composite to only elements that match the passed selector.
10916     * @param {String} selector A string CSS selector
10917     * @return {CompositeElement} this
10918     */
10919     filter : function(selector){
10920         var els = [];
10921         this.each(function(el){
10922             if(el.is(selector)){
10923                 els[els.length] = el.dom;
10924             }
10925         });
10926         this.fill(els);
10927         return this;
10928     },
10929
10930     invoke : function(fn, args){
10931         var els = this.elements;
10932         for(var i = 0, len = els.length; i < len; i++) {
10933                 Roo.Element.prototype[fn].apply(els[i], args);
10934         }
10935         return this;
10936     },
10937     /**
10938     * Adds elements to this composite.
10939     * @param {String/Array} els A string CSS selector, an array of elements or an element
10940     * @return {CompositeElement} this
10941     */
10942     add : function(els){
10943         if(typeof els == "string"){
10944             this.addElements(Roo.Element.selectorFunction(els));
10945         }else if(els.length !== undefined){
10946             this.addElements(els);
10947         }else{
10948             this.addElements([els]);
10949         }
10950         return this;
10951     },
10952     /**
10953     * Calls the passed function passing (el, this, index) for each element in this composite.
10954     * @param {Function} fn The function to call
10955     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10956     * @return {CompositeElement} this
10957     */
10958     each : function(fn, scope){
10959         var els = this.elements;
10960         for(var i = 0, len = els.length; i < len; i++){
10961             if(fn.call(scope || els[i], els[i], this, i) === false) {
10962                 break;
10963             }
10964         }
10965         return this;
10966     },
10967
10968     /**
10969      * Returns the Element object at the specified index
10970      * @param {Number} index
10971      * @return {Roo.Element}
10972      */
10973     item : function(index){
10974         return this.elements[index] || null;
10975     },
10976
10977     /**
10978      * Returns the first Element
10979      * @return {Roo.Element}
10980      */
10981     first : function(){
10982         return this.item(0);
10983     },
10984
10985     /**
10986      * Returns the last Element
10987      * @return {Roo.Element}
10988      */
10989     last : function(){
10990         return this.item(this.elements.length-1);
10991     },
10992
10993     /**
10994      * Returns the number of elements in this composite
10995      * @return Number
10996      */
10997     getCount : function(){
10998         return this.elements.length;
10999     },
11000
11001     /**
11002      * Returns true if this composite contains the passed element
11003      * @return Boolean
11004      */
11005     contains : function(el){
11006         return this.indexOf(el) !== -1;
11007     },
11008
11009     /**
11010      * Returns true if this composite contains the passed element
11011      * @return Boolean
11012      */
11013     indexOf : function(el){
11014         return this.elements.indexOf(Roo.get(el));
11015     },
11016
11017
11018     /**
11019     * Removes the specified element(s).
11020     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11021     * or an array of any of those.
11022     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11023     * @return {CompositeElement} this
11024     */
11025     removeElement : function(el, removeDom){
11026         if(el instanceof Array){
11027             for(var i = 0, len = el.length; i < len; i++){
11028                 this.removeElement(el[i]);
11029             }
11030             return this;
11031         }
11032         var index = typeof el == 'number' ? el : this.indexOf(el);
11033         if(index !== -1){
11034             if(removeDom){
11035                 var d = this.elements[index];
11036                 if(d.dom){
11037                     d.remove();
11038                 }else{
11039                     d.parentNode.removeChild(d);
11040                 }
11041             }
11042             this.elements.splice(index, 1);
11043         }
11044         return this;
11045     },
11046
11047     /**
11048     * Replaces the specified element with the passed element.
11049     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11050     * to replace.
11051     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11052     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11053     * @return {CompositeElement} this
11054     */
11055     replaceElement : function(el, replacement, domReplace){
11056         var index = typeof el == 'number' ? el : this.indexOf(el);
11057         if(index !== -1){
11058             if(domReplace){
11059                 this.elements[index].replaceWith(replacement);
11060             }else{
11061                 this.elements.splice(index, 1, Roo.get(replacement))
11062             }
11063         }
11064         return this;
11065     },
11066
11067     /**
11068      * Removes all elements.
11069      */
11070     clear : function(){
11071         this.elements = [];
11072     }
11073 };
11074 (function(){
11075     Roo.CompositeElement.createCall = function(proto, fnName){
11076         if(!proto[fnName]){
11077             proto[fnName] = function(){
11078                 return this.invoke(fnName, arguments);
11079             };
11080         }
11081     };
11082     for(var fnName in Roo.Element.prototype){
11083         if(typeof Roo.Element.prototype[fnName] == "function"){
11084             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11085         }
11086     };
11087 })();
11088 /*
11089  * Based on:
11090  * Ext JS Library 1.1.1
11091  * Copyright(c) 2006-2007, Ext JS, LLC.
11092  *
11093  * Originally Released Under LGPL - original licence link has changed is not relivant.
11094  *
11095  * Fork - LGPL
11096  * <script type="text/javascript">
11097  */
11098
11099 /**
11100  * @class Roo.CompositeElementLite
11101  * @extends Roo.CompositeElement
11102  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11103  <pre><code>
11104  var els = Roo.select("#some-el div.some-class");
11105  // or select directly from an existing element
11106  var el = Roo.get('some-el');
11107  el.select('div.some-class');
11108
11109  els.setWidth(100); // all elements become 100 width
11110  els.hide(true); // all elements fade out and hide
11111  // or
11112  els.setWidth(100).hide(true);
11113  </code></pre><br><br>
11114  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11115  * actions will be performed on all the elements in this collection.</b>
11116  */
11117 Roo.CompositeElementLite = function(els){
11118     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11119     this.el = new Roo.Element.Flyweight();
11120 };
11121 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11122     addElements : function(els){
11123         if(els){
11124             if(els instanceof Array){
11125                 this.elements = this.elements.concat(els);
11126             }else{
11127                 var yels = this.elements;
11128                 var index = yels.length-1;
11129                 for(var i = 0, len = els.length; i < len; i++) {
11130                     yels[++index] = els[i];
11131                 }
11132             }
11133         }
11134         return this;
11135     },
11136     invoke : function(fn, args){
11137         var els = this.elements;
11138         var el = this.el;
11139         for(var i = 0, len = els.length; i < len; i++) {
11140             el.dom = els[i];
11141                 Roo.Element.prototype[fn].apply(el, args);
11142         }
11143         return this;
11144     },
11145     /**
11146      * Returns a flyweight Element of the dom element object at the specified index
11147      * @param {Number} index
11148      * @return {Roo.Element}
11149      */
11150     item : function(index){
11151         if(!this.elements[index]){
11152             return null;
11153         }
11154         this.el.dom = this.elements[index];
11155         return this.el;
11156     },
11157
11158     // fixes scope with flyweight
11159     addListener : function(eventName, handler, scope, opt){
11160         var els = this.elements;
11161         for(var i = 0, len = els.length; i < len; i++) {
11162             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11163         }
11164         return this;
11165     },
11166
11167     /**
11168     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11169     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11170     * a reference to the dom node, use el.dom.</b>
11171     * @param {Function} fn The function to call
11172     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11173     * @return {CompositeElement} this
11174     */
11175     each : function(fn, scope){
11176         var els = this.elements;
11177         var el = this.el;
11178         for(var i = 0, len = els.length; i < len; i++){
11179             el.dom = els[i];
11180                 if(fn.call(scope || el, el, this, i) === false){
11181                 break;
11182             }
11183         }
11184         return this;
11185     },
11186
11187     indexOf : function(el){
11188         return this.elements.indexOf(Roo.getDom(el));
11189     },
11190
11191     replaceElement : function(el, replacement, domReplace){
11192         var index = typeof el == 'number' ? el : this.indexOf(el);
11193         if(index !== -1){
11194             replacement = Roo.getDom(replacement);
11195             if(domReplace){
11196                 var d = this.elements[index];
11197                 d.parentNode.insertBefore(replacement, d);
11198                 d.parentNode.removeChild(d);
11199             }
11200             this.elements.splice(index, 1, replacement);
11201         }
11202         return this;
11203     }
11204 });
11205 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11206
11207 /*
11208  * Based on:
11209  * Ext JS Library 1.1.1
11210  * Copyright(c) 2006-2007, Ext JS, LLC.
11211  *
11212  * Originally Released Under LGPL - original licence link has changed is not relivant.
11213  *
11214  * Fork - LGPL
11215  * <script type="text/javascript">
11216  */
11217
11218  
11219
11220 /**
11221  * @class Roo.data.Connection
11222  * @extends Roo.util.Observable
11223  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11224  * either to a configured URL, or to a URL specified at request time.<br><br>
11225  * <p>
11226  * Requests made by this class are asynchronous, and will return immediately. No data from
11227  * the server will be available to the statement immediately following the {@link #request} call.
11228  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11229  * <p>
11230  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11231  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11232  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11233  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11234  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11235  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11236  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11237  * standard DOM methods.
11238  * @constructor
11239  * @param {Object} config a configuration object.
11240  */
11241 Roo.data.Connection = function(config){
11242     Roo.apply(this, config);
11243     this.addEvents({
11244         /**
11245          * @event beforerequest
11246          * Fires before a network request is made to retrieve a data object.
11247          * @param {Connection} conn This Connection object.
11248          * @param {Object} options The options config object passed to the {@link #request} method.
11249          */
11250         "beforerequest" : true,
11251         /**
11252          * @event requestcomplete
11253          * Fires if the request was successfully completed.
11254          * @param {Connection} conn This Connection object.
11255          * @param {Object} response The XHR object containing the response data.
11256          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11257          * @param {Object} options The options config object passed to the {@link #request} method.
11258          */
11259         "requestcomplete" : true,
11260         /**
11261          * @event requestexception
11262          * Fires if an error HTTP status was returned from the server.
11263          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11264          * @param {Connection} conn This Connection object.
11265          * @param {Object} response The XHR object containing the response data.
11266          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11267          * @param {Object} options The options config object passed to the {@link #request} method.
11268          */
11269         "requestexception" : true
11270     });
11271     Roo.data.Connection.superclass.constructor.call(this);
11272 };
11273
11274 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11275     /**
11276      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11277      */
11278     /**
11279      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11280      * extra parameters to each request made by this object. (defaults to undefined)
11281      */
11282     /**
11283      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11284      *  to each request made by this object. (defaults to undefined)
11285      */
11286     /**
11287      * @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)
11288      */
11289     /**
11290      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11291      */
11292     timeout : 30000,
11293     /**
11294      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11295      * @type Boolean
11296      */
11297     autoAbort:false,
11298
11299     /**
11300      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11301      * @type Boolean
11302      */
11303     disableCaching: true,
11304
11305     /**
11306      * Sends an HTTP request to a remote server.
11307      * @param {Object} options An object which may contain the following properties:<ul>
11308      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11309      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11310      * request, a url encoded string or a function to call to get either.</li>
11311      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11312      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11313      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11314      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11315      * <li>options {Object} The parameter to the request call.</li>
11316      * <li>success {Boolean} True if the request succeeded.</li>
11317      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11318      * </ul></li>
11319      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11320      * The callback is passed the following parameters:<ul>
11321      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11322      * <li>options {Object} The parameter to the request call.</li>
11323      * </ul></li>
11324      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11325      * The callback is passed the following parameters:<ul>
11326      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11327      * <li>options {Object} The parameter to the request call.</li>
11328      * </ul></li>
11329      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11330      * for the callback function. Defaults to the browser window.</li>
11331      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11332      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11333      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11334      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11335      * params for the post data. Any params will be appended to the URL.</li>
11336      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11337      * </ul>
11338      * @return {Number} transactionId
11339      */
11340     request : function(o){
11341         if(this.fireEvent("beforerequest", this, o) !== false){
11342             var p = o.params;
11343
11344             if(typeof p == "function"){
11345                 p = p.call(o.scope||window, o);
11346             }
11347             if(typeof p == "object"){
11348                 p = Roo.urlEncode(o.params);
11349             }
11350             if(this.extraParams){
11351                 var extras = Roo.urlEncode(this.extraParams);
11352                 p = p ? (p + '&' + extras) : extras;
11353             }
11354
11355             var url = o.url || this.url;
11356             if(typeof url == 'function'){
11357                 url = url.call(o.scope||window, o);
11358             }
11359
11360             if(o.form){
11361                 var form = Roo.getDom(o.form);
11362                 url = url || form.action;
11363
11364                 var enctype = form.getAttribute("enctype");
11365                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11366                     return this.doFormUpload(o, p, url);
11367                 }
11368                 var f = Roo.lib.Ajax.serializeForm(form);
11369                 p = p ? (p + '&' + f) : f;
11370             }
11371
11372             var hs = o.headers;
11373             if(this.defaultHeaders){
11374                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11375                 if(!o.headers){
11376                     o.headers = hs;
11377                 }
11378             }
11379
11380             var cb = {
11381                 success: this.handleResponse,
11382                 failure: this.handleFailure,
11383                 scope: this,
11384                 argument: {options: o},
11385                 timeout : o.timeout || this.timeout
11386             };
11387
11388             var method = o.method||this.method||(p ? "POST" : "GET");
11389
11390             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11391                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11392             }
11393
11394             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11395                 if(o.autoAbort){
11396                     this.abort();
11397                 }
11398             }else if(this.autoAbort !== false){
11399                 this.abort();
11400             }
11401
11402             if((method == 'GET' && p) || o.xmlData){
11403                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11404                 p = '';
11405             }
11406             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11407             return this.transId;
11408         }else{
11409             Roo.callback(o.callback, o.scope, [o, null, null]);
11410             return null;
11411         }
11412     },
11413
11414     /**
11415      * Determine whether this object has a request outstanding.
11416      * @param {Number} transactionId (Optional) defaults to the last transaction
11417      * @return {Boolean} True if there is an outstanding request.
11418      */
11419     isLoading : function(transId){
11420         if(transId){
11421             return Roo.lib.Ajax.isCallInProgress(transId);
11422         }else{
11423             return this.transId ? true : false;
11424         }
11425     },
11426
11427     /**
11428      * Aborts any outstanding request.
11429      * @param {Number} transactionId (Optional) defaults to the last transaction
11430      */
11431     abort : function(transId){
11432         if(transId || this.isLoading()){
11433             Roo.lib.Ajax.abort(transId || this.transId);
11434         }
11435     },
11436
11437     // private
11438     handleResponse : function(response){
11439         this.transId = false;
11440         var options = response.argument.options;
11441         response.argument = options ? options.argument : null;
11442         this.fireEvent("requestcomplete", this, response, options);
11443         Roo.callback(options.success, options.scope, [response, options]);
11444         Roo.callback(options.callback, options.scope, [options, true, response]);
11445     },
11446
11447     // private
11448     handleFailure : function(response, e){
11449         this.transId = false;
11450         var options = response.argument.options;
11451         response.argument = options ? options.argument : null;
11452         this.fireEvent("requestexception", this, response, options, e);
11453         Roo.callback(options.failure, options.scope, [response, options]);
11454         Roo.callback(options.callback, options.scope, [options, false, response]);
11455     },
11456
11457     // private
11458     doFormUpload : function(o, ps, url){
11459         var id = Roo.id();
11460         var frame = document.createElement('iframe');
11461         frame.id = id;
11462         frame.name = id;
11463         frame.className = 'x-hidden';
11464         if(Roo.isIE){
11465             frame.src = Roo.SSL_SECURE_URL;
11466         }
11467         document.body.appendChild(frame);
11468
11469         if(Roo.isIE){
11470            document.frames[id].name = id;
11471         }
11472
11473         var form = Roo.getDom(o.form);
11474         form.target = id;
11475         form.method = 'POST';
11476         form.enctype = form.encoding = 'multipart/form-data';
11477         if(url){
11478             form.action = url;
11479         }
11480
11481         var hiddens, hd;
11482         if(ps){ // add dynamic params
11483             hiddens = [];
11484             ps = Roo.urlDecode(ps, false);
11485             for(var k in ps){
11486                 if(ps.hasOwnProperty(k)){
11487                     hd = document.createElement('input');
11488                     hd.type = 'hidden';
11489                     hd.name = k;
11490                     hd.value = ps[k];
11491                     form.appendChild(hd);
11492                     hiddens.push(hd);
11493                 }
11494             }
11495         }
11496
11497         function cb(){
11498             var r = {  // bogus response object
11499                 responseText : '',
11500                 responseXML : null
11501             };
11502
11503             r.argument = o ? o.argument : null;
11504
11505             try { //
11506                 var doc;
11507                 if(Roo.isIE){
11508                     doc = frame.contentWindow.document;
11509                 }else {
11510                     doc = (frame.contentDocument || window.frames[id].document);
11511                 }
11512                 if(doc && doc.body){
11513                     r.responseText = doc.body.innerHTML;
11514                 }
11515                 if(doc && doc.XMLDocument){
11516                     r.responseXML = doc.XMLDocument;
11517                 }else {
11518                     r.responseXML = doc;
11519                 }
11520             }
11521             catch(e) {
11522                 // ignore
11523             }
11524
11525             Roo.EventManager.removeListener(frame, 'load', cb, this);
11526
11527             this.fireEvent("requestcomplete", this, r, o);
11528             Roo.callback(o.success, o.scope, [r, o]);
11529             Roo.callback(o.callback, o.scope, [o, true, r]);
11530
11531             setTimeout(function(){document.body.removeChild(frame);}, 100);
11532         }
11533
11534         Roo.EventManager.on(frame, 'load', cb, this);
11535         form.submit();
11536
11537         if(hiddens){ // remove dynamic params
11538             for(var i = 0, len = hiddens.length; i < len; i++){
11539                 form.removeChild(hiddens[i]);
11540             }
11541         }
11542     }
11543 });
11544
11545 /**
11546  * @class Roo.Ajax
11547  * @extends Roo.data.Connection
11548  * Global Ajax request class.
11549  *
11550  * @singleton
11551  */
11552 Roo.Ajax = new Roo.data.Connection({
11553     // fix up the docs
11554    /**
11555      * @cfg {String} url @hide
11556      */
11557     /**
11558      * @cfg {Object} extraParams @hide
11559      */
11560     /**
11561      * @cfg {Object} defaultHeaders @hide
11562      */
11563     /**
11564      * @cfg {String} method (Optional) @hide
11565      */
11566     /**
11567      * @cfg {Number} timeout (Optional) @hide
11568      */
11569     /**
11570      * @cfg {Boolean} autoAbort (Optional) @hide
11571      */
11572
11573     /**
11574      * @cfg {Boolean} disableCaching (Optional) @hide
11575      */
11576
11577     /**
11578      * @property  disableCaching
11579      * True to add a unique cache-buster param to GET requests. (defaults to true)
11580      * @type Boolean
11581      */
11582     /**
11583      * @property  url
11584      * The default URL to be used for requests to the server. (defaults to undefined)
11585      * @type String
11586      */
11587     /**
11588      * @property  extraParams
11589      * An object containing properties which are used as
11590      * extra parameters to each request made by this object. (defaults to undefined)
11591      * @type Object
11592      */
11593     /**
11594      * @property  defaultHeaders
11595      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11596      * @type Object
11597      */
11598     /**
11599      * @property  method
11600      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11601      * @type String
11602      */
11603     /**
11604      * @property  timeout
11605      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11606      * @type Number
11607      */
11608
11609     /**
11610      * @property  autoAbort
11611      * Whether a new request should abort any pending requests. (defaults to false)
11612      * @type Boolean
11613      */
11614     autoAbort : false,
11615
11616     /**
11617      * Serialize the passed form into a url encoded string
11618      * @param {String/HTMLElement} form
11619      * @return {String}
11620      */
11621     serializeForm : function(form){
11622         return Roo.lib.Ajax.serializeForm(form);
11623     }
11624 });/*
11625  * Based on:
11626  * Ext JS Library 1.1.1
11627  * Copyright(c) 2006-2007, Ext JS, LLC.
11628  *
11629  * Originally Released Under LGPL - original licence link has changed is not relivant.
11630  *
11631  * Fork - LGPL
11632  * <script type="text/javascript">
11633  */
11634  
11635 /**
11636  * Global Ajax request class.
11637  * 
11638  * @class Roo.Ajax
11639  * @extends Roo.data.Connection
11640  * @static
11641  * 
11642  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11643  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11644  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11645  * @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)
11646  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11647  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11648  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11649  */
11650 Roo.Ajax = new Roo.data.Connection({
11651     // fix up the docs
11652     /**
11653      * @scope Roo.Ajax
11654      * @type {Boolear} 
11655      */
11656     autoAbort : false,
11657
11658     /**
11659      * Serialize the passed form into a url encoded string
11660      * @scope Roo.Ajax
11661      * @param {String/HTMLElement} form
11662      * @return {String}
11663      */
11664     serializeForm : function(form){
11665         return Roo.lib.Ajax.serializeForm(form);
11666     }
11667 });/*
11668  * Based on:
11669  * Ext JS Library 1.1.1
11670  * Copyright(c) 2006-2007, Ext JS, LLC.
11671  *
11672  * Originally Released Under LGPL - original licence link has changed is not relivant.
11673  *
11674  * Fork - LGPL
11675  * <script type="text/javascript">
11676  */
11677
11678  
11679 /**
11680  * @class Roo.UpdateManager
11681  * @extends Roo.util.Observable
11682  * Provides AJAX-style update for Element object.<br><br>
11683  * Usage:<br>
11684  * <pre><code>
11685  * // Get it from a Roo.Element object
11686  * var el = Roo.get("foo");
11687  * var mgr = el.getUpdateManager();
11688  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11689  * ...
11690  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11691  * <br>
11692  * // or directly (returns the same UpdateManager instance)
11693  * var mgr = new Roo.UpdateManager("myElementId");
11694  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11695  * mgr.on("update", myFcnNeedsToKnow);
11696  * <br>
11697    // short handed call directly from the element object
11698    Roo.get("foo").load({
11699         url: "bar.php",
11700         scripts:true,
11701         params: "for=bar",
11702         text: "Loading Foo..."
11703    });
11704  * </code></pre>
11705  * @constructor
11706  * Create new UpdateManager directly.
11707  * @param {String/HTMLElement/Roo.Element} el The element to update
11708  * @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).
11709  */
11710 Roo.UpdateManager = function(el, forceNew){
11711     el = Roo.get(el);
11712     if(!forceNew && el.updateManager){
11713         return el.updateManager;
11714     }
11715     /**
11716      * The Element object
11717      * @type Roo.Element
11718      */
11719     this.el = el;
11720     /**
11721      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11722      * @type String
11723      */
11724     this.defaultUrl = null;
11725
11726     this.addEvents({
11727         /**
11728          * @event beforeupdate
11729          * Fired before an update is made, return false from your handler and the update is cancelled.
11730          * @param {Roo.Element} el
11731          * @param {String/Object/Function} url
11732          * @param {String/Object} params
11733          */
11734         "beforeupdate": true,
11735         /**
11736          * @event update
11737          * Fired after successful update is made.
11738          * @param {Roo.Element} el
11739          * @param {Object} oResponseObject The response Object
11740          */
11741         "update": true,
11742         /**
11743          * @event failure
11744          * Fired on update failure.
11745          * @param {Roo.Element} el
11746          * @param {Object} oResponseObject The response Object
11747          */
11748         "failure": true
11749     });
11750     var d = Roo.UpdateManager.defaults;
11751     /**
11752      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11753      * @type String
11754      */
11755     this.sslBlankUrl = d.sslBlankUrl;
11756     /**
11757      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11758      * @type Boolean
11759      */
11760     this.disableCaching = d.disableCaching;
11761     /**
11762      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11763      * @type String
11764      */
11765     this.indicatorText = d.indicatorText;
11766     /**
11767      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11768      * @type String
11769      */
11770     this.showLoadIndicator = d.showLoadIndicator;
11771     /**
11772      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11773      * @type Number
11774      */
11775     this.timeout = d.timeout;
11776
11777     /**
11778      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11779      * @type Boolean
11780      */
11781     this.loadScripts = d.loadScripts;
11782
11783     /**
11784      * Transaction object of current executing transaction
11785      */
11786     this.transaction = null;
11787
11788     /**
11789      * @private
11790      */
11791     this.autoRefreshProcId = null;
11792     /**
11793      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11794      * @type Function
11795      */
11796     this.refreshDelegate = this.refresh.createDelegate(this);
11797     /**
11798      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11799      * @type Function
11800      */
11801     this.updateDelegate = this.update.createDelegate(this);
11802     /**
11803      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11804      * @type Function
11805      */
11806     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11807     /**
11808      * @private
11809      */
11810     this.successDelegate = this.processSuccess.createDelegate(this);
11811     /**
11812      * @private
11813      */
11814     this.failureDelegate = this.processFailure.createDelegate(this);
11815
11816     if(!this.renderer){
11817      /**
11818       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11819       */
11820     this.renderer = new Roo.UpdateManager.BasicRenderer();
11821     }
11822     
11823     Roo.UpdateManager.superclass.constructor.call(this);
11824 };
11825
11826 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11827     /**
11828      * Get the Element this UpdateManager is bound to
11829      * @return {Roo.Element} The element
11830      */
11831     getEl : function(){
11832         return this.el;
11833     },
11834     /**
11835      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11836      * @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:
11837 <pre><code>
11838 um.update({<br/>
11839     url: "your-url.php",<br/>
11840     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11841     callback: yourFunction,<br/>
11842     scope: yourObject, //(optional scope)  <br/>
11843     discardUrl: false, <br/>
11844     nocache: false,<br/>
11845     text: "Loading...",<br/>
11846     timeout: 30,<br/>
11847     scripts: false<br/>
11848 });
11849 </code></pre>
11850      * The only required property is url. The optional properties nocache, text and scripts
11851      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11852      * @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}
11853      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11854      * @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.
11855      */
11856     update : function(url, params, callback, discardUrl){
11857         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11858             var method = this.method, cfg;
11859             if(typeof url == "object"){ // must be config object
11860                 cfg = url;
11861                 url = cfg.url;
11862                 params = params || cfg.params;
11863                 callback = callback || cfg.callback;
11864                 discardUrl = discardUrl || cfg.discardUrl;
11865                 if(callback && cfg.scope){
11866                     callback = callback.createDelegate(cfg.scope);
11867                 }
11868                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11869                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11870                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11871                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11872                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11873             }
11874             this.showLoading();
11875             if(!discardUrl){
11876                 this.defaultUrl = url;
11877             }
11878             if(typeof url == "function"){
11879                 url = url.call(this);
11880             }
11881
11882             method = method || (params ? "POST" : "GET");
11883             if(method == "GET"){
11884                 url = this.prepareUrl(url);
11885             }
11886
11887             var o = Roo.apply(cfg ||{}, {
11888                 url : url,
11889                 params: params,
11890                 success: this.successDelegate,
11891                 failure: this.failureDelegate,
11892                 callback: undefined,
11893                 timeout: (this.timeout*1000),
11894                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11895             });
11896
11897             this.transaction = Roo.Ajax.request(o);
11898         }
11899     },
11900
11901     /**
11902      * 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.
11903      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11904      * @param {String/HTMLElement} form The form Id or form element
11905      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11906      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11907      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11908      */
11909     formUpdate : function(form, url, reset, callback){
11910         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11911             if(typeof url == "function"){
11912                 url = url.call(this);
11913             }
11914             form = Roo.getDom(form);
11915             this.transaction = Roo.Ajax.request({
11916                 form: form,
11917                 url:url,
11918                 success: this.successDelegate,
11919                 failure: this.failureDelegate,
11920                 timeout: (this.timeout*1000),
11921                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11922             });
11923             this.showLoading.defer(1, this);
11924         }
11925     },
11926
11927     /**
11928      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11929      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11930      */
11931     refresh : function(callback){
11932         if(this.defaultUrl == null){
11933             return;
11934         }
11935         this.update(this.defaultUrl, null, callback, true);
11936     },
11937
11938     /**
11939      * Set this element to auto refresh.
11940      * @param {Number} interval How often to update (in seconds).
11941      * @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)
11942      * @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}
11943      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11944      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11945      */
11946     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11947         if(refreshNow){
11948             this.update(url || this.defaultUrl, params, callback, true);
11949         }
11950         if(this.autoRefreshProcId){
11951             clearInterval(this.autoRefreshProcId);
11952         }
11953         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11954     },
11955
11956     /**
11957      * Stop auto refresh on this element.
11958      */
11959      stopAutoRefresh : function(){
11960         if(this.autoRefreshProcId){
11961             clearInterval(this.autoRefreshProcId);
11962             delete this.autoRefreshProcId;
11963         }
11964     },
11965
11966     isAutoRefreshing : function(){
11967        return this.autoRefreshProcId ? true : false;
11968     },
11969     /**
11970      * Called to update the element to "Loading" state. Override to perform custom action.
11971      */
11972     showLoading : function(){
11973         if(this.showLoadIndicator){
11974             this.el.update(this.indicatorText);
11975         }
11976     },
11977
11978     /**
11979      * Adds unique parameter to query string if disableCaching = true
11980      * @private
11981      */
11982     prepareUrl : function(url){
11983         if(this.disableCaching){
11984             var append = "_dc=" + (new Date().getTime());
11985             if(url.indexOf("?") !== -1){
11986                 url += "&" + append;
11987             }else{
11988                 url += "?" + append;
11989             }
11990         }
11991         return url;
11992     },
11993
11994     /**
11995      * @private
11996      */
11997     processSuccess : function(response){
11998         this.transaction = null;
11999         if(response.argument.form && response.argument.reset){
12000             try{ // put in try/catch since some older FF releases had problems with this
12001                 response.argument.form.reset();
12002             }catch(e){}
12003         }
12004         if(this.loadScripts){
12005             this.renderer.render(this.el, response, this,
12006                 this.updateComplete.createDelegate(this, [response]));
12007         }else{
12008             this.renderer.render(this.el, response, this);
12009             this.updateComplete(response);
12010         }
12011     },
12012
12013     updateComplete : function(response){
12014         this.fireEvent("update", this.el, response);
12015         if(typeof response.argument.callback == "function"){
12016             response.argument.callback(this.el, true, response);
12017         }
12018     },
12019
12020     /**
12021      * @private
12022      */
12023     processFailure : function(response){
12024         this.transaction = null;
12025         this.fireEvent("failure", this.el, response);
12026         if(typeof response.argument.callback == "function"){
12027             response.argument.callback(this.el, false, response);
12028         }
12029     },
12030
12031     /**
12032      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12033      * @param {Object} renderer The object implementing the render() method
12034      */
12035     setRenderer : function(renderer){
12036         this.renderer = renderer;
12037     },
12038
12039     getRenderer : function(){
12040        return this.renderer;
12041     },
12042
12043     /**
12044      * Set the defaultUrl used for updates
12045      * @param {String/Function} defaultUrl The url or a function to call to get the url
12046      */
12047     setDefaultUrl : function(defaultUrl){
12048         this.defaultUrl = defaultUrl;
12049     },
12050
12051     /**
12052      * Aborts the executing transaction
12053      */
12054     abort : function(){
12055         if(this.transaction){
12056             Roo.Ajax.abort(this.transaction);
12057         }
12058     },
12059
12060     /**
12061      * Returns true if an update is in progress
12062      * @return {Boolean}
12063      */
12064     isUpdating : function(){
12065         if(this.transaction){
12066             return Roo.Ajax.isLoading(this.transaction);
12067         }
12068         return false;
12069     }
12070 });
12071
12072 /**
12073  * @class Roo.UpdateManager.defaults
12074  * @static (not really - but it helps the doc tool)
12075  * The defaults collection enables customizing the default properties of UpdateManager
12076  */
12077    Roo.UpdateManager.defaults = {
12078        /**
12079          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12080          * @type Number
12081          */
12082          timeout : 30,
12083
12084          /**
12085          * True to process scripts by default (Defaults to false).
12086          * @type Boolean
12087          */
12088         loadScripts : false,
12089
12090         /**
12091         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12092         * @type String
12093         */
12094         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12095         /**
12096          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12097          * @type Boolean
12098          */
12099         disableCaching : false,
12100         /**
12101          * Whether to show indicatorText when loading (Defaults to true).
12102          * @type Boolean
12103          */
12104         showLoadIndicator : true,
12105         /**
12106          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12107          * @type String
12108          */
12109         indicatorText : '<div class="loading-indicator">Loading...</div>'
12110    };
12111
12112 /**
12113  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12114  *Usage:
12115  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12116  * @param {String/HTMLElement/Roo.Element} el The element to update
12117  * @param {String} url The url
12118  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12119  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12120  * @static
12121  * @deprecated
12122  * @member Roo.UpdateManager
12123  */
12124 Roo.UpdateManager.updateElement = function(el, url, params, options){
12125     var um = Roo.get(el, true).getUpdateManager();
12126     Roo.apply(um, options);
12127     um.update(url, params, options ? options.callback : null);
12128 };
12129 // alias for backwards compat
12130 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12131 /**
12132  * @class Roo.UpdateManager.BasicRenderer
12133  * Default Content renderer. Updates the elements innerHTML with the responseText.
12134  */
12135 Roo.UpdateManager.BasicRenderer = function(){};
12136
12137 Roo.UpdateManager.BasicRenderer.prototype = {
12138     /**
12139      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12140      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12141      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12142      * @param {Roo.Element} el The element being rendered
12143      * @param {Object} response The YUI Connect response object
12144      * @param {UpdateManager} updateManager The calling update manager
12145      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12146      */
12147      render : function(el, response, updateManager, callback){
12148         el.update(response.responseText, updateManager.loadScripts, callback);
12149     }
12150 };
12151 /*
12152  * Based on:
12153  * Roo JS
12154  * (c)) Alan Knowles
12155  * Licence : LGPL
12156  */
12157
12158
12159 /**
12160  * @class Roo.DomTemplate
12161  * @extends Roo.Template
12162  * An effort at a dom based template engine..
12163  *
12164  * Similar to XTemplate, except it uses dom parsing to create the template..
12165  *
12166  * Supported features:
12167  *
12168  *  Tags:
12169
12170 <pre><code>
12171       {a_variable} - output encoded.
12172       {a_variable.format:("Y-m-d")} - call a method on the variable
12173       {a_variable:raw} - unencoded output
12174       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12175       {a_variable:this.method_on_template(...)} - call a method on the template object.
12176  
12177 </code></pre>
12178  *  The tpl tag:
12179 <pre><code>
12180         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12181         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12182         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12183         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12184   
12185 </code></pre>
12186  *      
12187  */
12188 Roo.DomTemplate = function()
12189 {
12190      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12191      if (this.html) {
12192         this.compile();
12193      }
12194 };
12195
12196
12197 Roo.extend(Roo.DomTemplate, Roo.Template, {
12198     /**
12199      * id counter for sub templates.
12200      */
12201     id : 0,
12202     /**
12203      * flag to indicate if dom parser is inside a pre,
12204      * it will strip whitespace if not.
12205      */
12206     inPre : false,
12207     
12208     /**
12209      * The various sub templates
12210      */
12211     tpls : false,
12212     
12213     
12214     
12215     /**
12216      *
12217      * basic tag replacing syntax
12218      * WORD:WORD()
12219      *
12220      * // you can fake an object call by doing this
12221      *  x.t:(test,tesT) 
12222      * 
12223      */
12224     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\}|\%7D)/g,
12225     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12226     
12227     iterChild : function (node, method) {
12228         
12229         var oldPre = this.inPre;
12230         if (node.tagName == 'PRE') {
12231             this.inPre = true;
12232         }
12233         for( var i = 0; i < node.childNodes.length; i++) {
12234             method.call(this, node.childNodes[i]);
12235         }
12236         this.inPre = oldPre;
12237     },
12238     
12239     
12240     
12241     /**
12242      * compile the template
12243      *
12244      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12245      *
12246      */
12247     compile: function()
12248     {
12249         var s = this.html;
12250         
12251         // covert the html into DOM...
12252         var doc = document.implementation.createHTMLDocument("");
12253         //doc.documentElement.innerHTML = htmlBody
12254         var div = doc.documentElement;
12255         div.innerHTML =   this.html  ;
12256         
12257         this.tpls = [];
12258         var _t = this;
12259         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12260         
12261         var tpls = this.tpls;
12262         
12263         // create a top level template from the snippet..
12264         
12265         //Roo.log(div.innerHTML);
12266         
12267         var tpl = {
12268             uid : 'master',
12269             id : this.id++,
12270             attr : false,
12271             value : false,
12272             body : div.innerHTML,
12273             
12274             forCall : false,
12275             execCall : false,
12276             dom : div,
12277             isTop : true
12278             
12279         };
12280         tpls.unshift(tpl);
12281         
12282         
12283         // compile them...
12284         this.tpls = [];
12285         Roo.each(tpls, function(tp){
12286             this.compileTpl(tp);
12287             this.tpls[tp.id] = tp;
12288         }, this);
12289         
12290         this.master = tpls[0];
12291         return this;
12292         
12293         
12294     },
12295     
12296     compileNode : function(node, istop) {
12297         // test for
12298         //Roo.log(node);
12299         
12300         
12301         // skip anything not a tag..
12302         if (node.nodeType != 1) {
12303             if (node.nodeType == 3 && !this.inPre) {
12304                 // reduce white space..
12305                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12306                 
12307             }
12308             return;
12309         }
12310         
12311         var tpl = {
12312             uid : false,
12313             id : false,
12314             attr : false,
12315             value : false,
12316             body : '',
12317             
12318             forCall : false,
12319             execCall : false,
12320             dom : false,
12321             isTop : istop
12322             
12323             
12324         };
12325         
12326         
12327         switch(true) {
12328             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12329             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12330             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12331             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12332             // no default..
12333         }
12334         
12335         
12336         if (!tpl.attr) {
12337             // just itterate children..
12338             this.iterChild(node,this.compileNode);
12339             return;
12340         }
12341         tpl.uid = this.id++;
12342         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12343         node.removeAttribute('roo-'+ tpl.attr);
12344         if (tpl.attr != 'name') {
12345             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12346             node.parentNode.replaceChild(placeholder,  node);
12347         } else {
12348             
12349             var placeholder =  document.createElement('span');
12350             placeholder.className = 'roo-tpl-' + tpl.value;
12351             node.parentNode.replaceChild(placeholder,  node);
12352         }
12353         
12354         // parent now sees '{domtplXXXX}
12355         this.iterChild(node,this.compileNode);
12356         
12357         // we should now have node body...
12358         var div = document.createElement('div');
12359         div.appendChild(node);
12360         tpl.dom = node;
12361         // this has the unfortunate side effect of converting tagged attributes
12362         // eg. href="{...}" into %7C...%7D
12363         // this has been fixed by searching for those combo's although it's a bit hacky..
12364         
12365         
12366         tpl.body = div.innerHTML;
12367         
12368         
12369          
12370         tpl.id = tpl.uid;
12371         switch(tpl.attr) {
12372             case 'for' :
12373                 switch (tpl.value) {
12374                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12375                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12376                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12377                 }
12378                 break;
12379             
12380             case 'exec':
12381                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12382                 break;
12383             
12384             case 'if':     
12385                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12386                 break;
12387             
12388             case 'name':
12389                 tpl.id  = tpl.value; // replace non characters???
12390                 break;
12391             
12392         }
12393         
12394         
12395         this.tpls.push(tpl);
12396         
12397         
12398         
12399     },
12400     
12401     
12402     
12403     
12404     /**
12405      * Compile a segment of the template into a 'sub-template'
12406      *
12407      * 
12408      * 
12409      *
12410      */
12411     compileTpl : function(tpl)
12412     {
12413         var fm = Roo.util.Format;
12414         var useF = this.disableFormats !== true;
12415         
12416         var sep = Roo.isGecko ? "+\n" : ",\n";
12417         
12418         var undef = function(str) {
12419             Roo.debug && Roo.log("Property not found :"  + str);
12420             return '';
12421         };
12422           
12423         //Roo.log(tpl.body);
12424         
12425         
12426         
12427         var fn = function(m, lbrace, name, format, args)
12428         {
12429             //Roo.log("ARGS");
12430             //Roo.log(arguments);
12431             args = args ? args.replace(/\\'/g,"'") : args;
12432             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12433             if (typeof(format) == 'undefined') {
12434                 format =  'htmlEncode'; 
12435             }
12436             if (format == 'raw' ) {
12437                 format = false;
12438             }
12439             
12440             if(name.substr(0, 6) == 'domtpl'){
12441                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12442             }
12443             
12444             // build an array of options to determine if value is undefined..
12445             
12446             // basically get 'xxxx.yyyy' then do
12447             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12448             //    (function () { Roo.log("Property not found"); return ''; })() :
12449             //    ......
12450             
12451             var udef_ar = [];
12452             var lookfor = '';
12453             Roo.each(name.split('.'), function(st) {
12454                 lookfor += (lookfor.length ? '.': '') + st;
12455                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12456             });
12457             
12458             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12459             
12460             
12461             if(format && useF){
12462                 
12463                 args = args ? ',' + args : "";
12464                  
12465                 if(format.substr(0, 5) != "this."){
12466                     format = "fm." + format + '(';
12467                 }else{
12468                     format = 'this.call("'+ format.substr(5) + '", ';
12469                     args = ", values";
12470                 }
12471                 
12472                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12473             }
12474              
12475             if (args.length) {
12476                 // called with xxyx.yuu:(test,test)
12477                 // change to ()
12478                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12479             }
12480             // raw.. - :raw modifier..
12481             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12482             
12483         };
12484         var body;
12485         // branched to use + in gecko and [].join() in others
12486         if(Roo.isGecko){
12487             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12488                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12489                     "';};};";
12490         }else{
12491             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12492             body.push(tpl.body.replace(/(\r\n|\n)/g,
12493                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12494             body.push("'].join('');};};");
12495             body = body.join('');
12496         }
12497         
12498         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12499        
12500         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12501         eval(body);
12502         
12503         return this;
12504     },
12505      
12506     /**
12507      * same as applyTemplate, except it's done to one of the subTemplates
12508      * when using named templates, you can do:
12509      *
12510      * var str = pl.applySubTemplate('your-name', values);
12511      *
12512      * 
12513      * @param {Number} id of the template
12514      * @param {Object} values to apply to template
12515      * @param {Object} parent (normaly the instance of this object)
12516      */
12517     applySubTemplate : function(id, values, parent)
12518     {
12519         
12520         
12521         var t = this.tpls[id];
12522         
12523         
12524         try { 
12525             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12526                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12527                 return '';
12528             }
12529         } catch(e) {
12530             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12531             Roo.log(values);
12532           
12533             return '';
12534         }
12535         try { 
12536             
12537             if(t.execCall && t.execCall.call(this, values, parent)){
12538                 return '';
12539             }
12540         } catch(e) {
12541             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12542             Roo.log(values);
12543             return '';
12544         }
12545         
12546         try {
12547             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12548             parent = t.target ? values : parent;
12549             if(t.forCall && vs instanceof Array){
12550                 var buf = [];
12551                 for(var i = 0, len = vs.length; i < len; i++){
12552                     try {
12553                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12554                     } catch (e) {
12555                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12556                         Roo.log(e.body);
12557                         //Roo.log(t.compiled);
12558                         Roo.log(vs[i]);
12559                     }   
12560                 }
12561                 return buf.join('');
12562             }
12563         } catch (e) {
12564             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12565             Roo.log(values);
12566             return '';
12567         }
12568         try {
12569             return t.compiled.call(this, vs, parent);
12570         } catch (e) {
12571             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12572             Roo.log(e.body);
12573             //Roo.log(t.compiled);
12574             Roo.log(values);
12575             return '';
12576         }
12577     },
12578
12579    
12580
12581     applyTemplate : function(values){
12582         return this.master.compiled.call(this, values, {});
12583         //var s = this.subs;
12584     },
12585
12586     apply : function(){
12587         return this.applyTemplate.apply(this, arguments);
12588     }
12589
12590  });
12591
12592 Roo.DomTemplate.from = function(el){
12593     el = Roo.getDom(el);
12594     return new Roo.Domtemplate(el.value || el.innerHTML);
12595 };/*
12596  * Based on:
12597  * Ext JS Library 1.1.1
12598  * Copyright(c) 2006-2007, Ext JS, LLC.
12599  *
12600  * Originally Released Under LGPL - original licence link has changed is not relivant.
12601  *
12602  * Fork - LGPL
12603  * <script type="text/javascript">
12604  */
12605
12606 /**
12607  * @class Roo.util.DelayedTask
12608  * Provides a convenient method of performing setTimeout where a new
12609  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12610  * You can use this class to buffer
12611  * the keypress events for a certain number of milliseconds, and perform only if they stop
12612  * for that amount of time.
12613  * @constructor The parameters to this constructor serve as defaults and are not required.
12614  * @param {Function} fn (optional) The default function to timeout
12615  * @param {Object} scope (optional) The default scope of that timeout
12616  * @param {Array} args (optional) The default Array of arguments
12617  */
12618 Roo.util.DelayedTask = function(fn, scope, args){
12619     var id = null, d, t;
12620
12621     var call = function(){
12622         var now = new Date().getTime();
12623         if(now - t >= d){
12624             clearInterval(id);
12625             id = null;
12626             fn.apply(scope, args || []);
12627         }
12628     };
12629     /**
12630      * Cancels any pending timeout and queues a new one
12631      * @param {Number} delay The milliseconds to delay
12632      * @param {Function} newFn (optional) Overrides function passed to constructor
12633      * @param {Object} newScope (optional) Overrides scope passed to constructor
12634      * @param {Array} newArgs (optional) Overrides args passed to constructor
12635      */
12636     this.delay = function(delay, newFn, newScope, newArgs){
12637         if(id && delay != d){
12638             this.cancel();
12639         }
12640         d = delay;
12641         t = new Date().getTime();
12642         fn = newFn || fn;
12643         scope = newScope || scope;
12644         args = newArgs || args;
12645         if(!id){
12646             id = setInterval(call, d);
12647         }
12648     };
12649
12650     /**
12651      * Cancel the last queued timeout
12652      */
12653     this.cancel = function(){
12654         if(id){
12655             clearInterval(id);
12656             id = null;
12657         }
12658     };
12659 };/*
12660  * Based on:
12661  * Ext JS Library 1.1.1
12662  * Copyright(c) 2006-2007, Ext JS, LLC.
12663  *
12664  * Originally Released Under LGPL - original licence link has changed is not relivant.
12665  *
12666  * Fork - LGPL
12667  * <script type="text/javascript">
12668  */
12669  
12670  
12671 Roo.util.TaskRunner = function(interval){
12672     interval = interval || 10;
12673     var tasks = [], removeQueue = [];
12674     var id = 0;
12675     var running = false;
12676
12677     var stopThread = function(){
12678         running = false;
12679         clearInterval(id);
12680         id = 0;
12681     };
12682
12683     var startThread = function(){
12684         if(!running){
12685             running = true;
12686             id = setInterval(runTasks, interval);
12687         }
12688     };
12689
12690     var removeTask = function(task){
12691         removeQueue.push(task);
12692         if(task.onStop){
12693             task.onStop();
12694         }
12695     };
12696
12697     var runTasks = function(){
12698         if(removeQueue.length > 0){
12699             for(var i = 0, len = removeQueue.length; i < len; i++){
12700                 tasks.remove(removeQueue[i]);
12701             }
12702             removeQueue = [];
12703             if(tasks.length < 1){
12704                 stopThread();
12705                 return;
12706             }
12707         }
12708         var now = new Date().getTime();
12709         for(var i = 0, len = tasks.length; i < len; ++i){
12710             var t = tasks[i];
12711             var itime = now - t.taskRunTime;
12712             if(t.interval <= itime){
12713                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12714                 t.taskRunTime = now;
12715                 if(rt === false || t.taskRunCount === t.repeat){
12716                     removeTask(t);
12717                     return;
12718                 }
12719             }
12720             if(t.duration && t.duration <= (now - t.taskStartTime)){
12721                 removeTask(t);
12722             }
12723         }
12724     };
12725
12726     /**
12727      * Queues a new task.
12728      * @param {Object} task
12729      */
12730     this.start = function(task){
12731         tasks.push(task);
12732         task.taskStartTime = new Date().getTime();
12733         task.taskRunTime = 0;
12734         task.taskRunCount = 0;
12735         startThread();
12736         return task;
12737     };
12738
12739     this.stop = function(task){
12740         removeTask(task);
12741         return task;
12742     };
12743
12744     this.stopAll = function(){
12745         stopThread();
12746         for(var i = 0, len = tasks.length; i < len; i++){
12747             if(tasks[i].onStop){
12748                 tasks[i].onStop();
12749             }
12750         }
12751         tasks = [];
12752         removeQueue = [];
12753     };
12754 };
12755
12756 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12757  * Based on:
12758  * Ext JS Library 1.1.1
12759  * Copyright(c) 2006-2007, Ext JS, LLC.
12760  *
12761  * Originally Released Under LGPL - original licence link has changed is not relivant.
12762  *
12763  * Fork - LGPL
12764  * <script type="text/javascript">
12765  */
12766
12767  
12768 /**
12769  * @class Roo.util.MixedCollection
12770  * @extends Roo.util.Observable
12771  * A Collection class that maintains both numeric indexes and keys and exposes events.
12772  * @constructor
12773  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12774  * collection (defaults to false)
12775  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12776  * and return the key value for that item.  This is used when available to look up the key on items that
12777  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12778  * equivalent to providing an implementation for the {@link #getKey} method.
12779  */
12780 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12781     this.items = [];
12782     this.map = {};
12783     this.keys = [];
12784     this.length = 0;
12785     this.addEvents({
12786         /**
12787          * @event clear
12788          * Fires when the collection is cleared.
12789          */
12790         "clear" : true,
12791         /**
12792          * @event add
12793          * Fires when an item is added to the collection.
12794          * @param {Number} index The index at which the item was added.
12795          * @param {Object} o The item added.
12796          * @param {String} key The key associated with the added item.
12797          */
12798         "add" : true,
12799         /**
12800          * @event replace
12801          * Fires when an item is replaced in the collection.
12802          * @param {String} key he key associated with the new added.
12803          * @param {Object} old The item being replaced.
12804          * @param {Object} new The new item.
12805          */
12806         "replace" : true,
12807         /**
12808          * @event remove
12809          * Fires when an item is removed from the collection.
12810          * @param {Object} o The item being removed.
12811          * @param {String} key (optional) The key associated with the removed item.
12812          */
12813         "remove" : true,
12814         "sort" : true
12815     });
12816     this.allowFunctions = allowFunctions === true;
12817     if(keyFn){
12818         this.getKey = keyFn;
12819     }
12820     Roo.util.MixedCollection.superclass.constructor.call(this);
12821 };
12822
12823 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12824     allowFunctions : false,
12825     
12826 /**
12827  * Adds an item to the collection.
12828  * @param {String} key The key to associate with the item
12829  * @param {Object} o The item to add.
12830  * @return {Object} The item added.
12831  */
12832     add : function(key, o){
12833         if(arguments.length == 1){
12834             o = arguments[0];
12835             key = this.getKey(o);
12836         }
12837         if(typeof key == "undefined" || key === null){
12838             this.length++;
12839             this.items.push(o);
12840             this.keys.push(null);
12841         }else{
12842             var old = this.map[key];
12843             if(old){
12844                 return this.replace(key, o);
12845             }
12846             this.length++;
12847             this.items.push(o);
12848             this.map[key] = o;
12849             this.keys.push(key);
12850         }
12851         this.fireEvent("add", this.length-1, o, key);
12852         return o;
12853     },
12854        
12855 /**
12856   * MixedCollection has a generic way to fetch keys if you implement getKey.
12857 <pre><code>
12858 // normal way
12859 var mc = new Roo.util.MixedCollection();
12860 mc.add(someEl.dom.id, someEl);
12861 mc.add(otherEl.dom.id, otherEl);
12862 //and so on
12863
12864 // using getKey
12865 var mc = new Roo.util.MixedCollection();
12866 mc.getKey = function(el){
12867    return el.dom.id;
12868 };
12869 mc.add(someEl);
12870 mc.add(otherEl);
12871
12872 // or via the constructor
12873 var mc = new Roo.util.MixedCollection(false, function(el){
12874    return el.dom.id;
12875 });
12876 mc.add(someEl);
12877 mc.add(otherEl);
12878 </code></pre>
12879  * @param o {Object} The item for which to find the key.
12880  * @return {Object} The key for the passed item.
12881  */
12882     getKey : function(o){
12883          return o.id; 
12884     },
12885    
12886 /**
12887  * Replaces an item in the collection.
12888  * @param {String} key The key associated with the item to replace, or the item to replace.
12889  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12890  * @return {Object}  The new item.
12891  */
12892     replace : function(key, o){
12893         if(arguments.length == 1){
12894             o = arguments[0];
12895             key = this.getKey(o);
12896         }
12897         var old = this.item(key);
12898         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12899              return this.add(key, o);
12900         }
12901         var index = this.indexOfKey(key);
12902         this.items[index] = o;
12903         this.map[key] = o;
12904         this.fireEvent("replace", key, old, o);
12905         return o;
12906     },
12907    
12908 /**
12909  * Adds all elements of an Array or an Object to the collection.
12910  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12911  * an Array of values, each of which are added to the collection.
12912  */
12913     addAll : function(objs){
12914         if(arguments.length > 1 || objs instanceof Array){
12915             var args = arguments.length > 1 ? arguments : objs;
12916             for(var i = 0, len = args.length; i < len; i++){
12917                 this.add(args[i]);
12918             }
12919         }else{
12920             for(var key in objs){
12921                 if(this.allowFunctions || typeof objs[key] != "function"){
12922                     this.add(key, objs[key]);
12923                 }
12924             }
12925         }
12926     },
12927    
12928 /**
12929  * Executes the specified function once for every item in the collection, passing each
12930  * item as the first and only parameter. returning false from the function will stop the iteration.
12931  * @param {Function} fn The function to execute for each item.
12932  * @param {Object} scope (optional) The scope in which to execute the function.
12933  */
12934     each : function(fn, scope){
12935         var items = [].concat(this.items); // each safe for removal
12936         for(var i = 0, len = items.length; i < len; i++){
12937             if(fn.call(scope || items[i], items[i], i, len) === false){
12938                 break;
12939             }
12940         }
12941     },
12942    
12943 /**
12944  * Executes the specified function once for every key in the collection, passing each
12945  * key, and its associated item as the first two parameters.
12946  * @param {Function} fn The function to execute for each item.
12947  * @param {Object} scope (optional) The scope in which to execute the function.
12948  */
12949     eachKey : function(fn, scope){
12950         for(var i = 0, len = this.keys.length; i < len; i++){
12951             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12952         }
12953     },
12954    
12955 /**
12956  * Returns the first item in the collection which elicits a true return value from the
12957  * passed selection function.
12958  * @param {Function} fn The selection function to execute for each item.
12959  * @param {Object} scope (optional) The scope in which to execute the function.
12960  * @return {Object} The first item in the collection which returned true from the selection function.
12961  */
12962     find : function(fn, scope){
12963         for(var i = 0, len = this.items.length; i < len; i++){
12964             if(fn.call(scope || window, this.items[i], this.keys[i])){
12965                 return this.items[i];
12966             }
12967         }
12968         return null;
12969     },
12970    
12971 /**
12972  * Inserts an item at the specified index in the collection.
12973  * @param {Number} index The index to insert the item at.
12974  * @param {String} key The key to associate with the new item, or the item itself.
12975  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12976  * @return {Object} The item inserted.
12977  */
12978     insert : function(index, key, o){
12979         if(arguments.length == 2){
12980             o = arguments[1];
12981             key = this.getKey(o);
12982         }
12983         if(index >= this.length){
12984             return this.add(key, o);
12985         }
12986         this.length++;
12987         this.items.splice(index, 0, o);
12988         if(typeof key != "undefined" && key != null){
12989             this.map[key] = o;
12990         }
12991         this.keys.splice(index, 0, key);
12992         this.fireEvent("add", index, o, key);
12993         return o;
12994     },
12995    
12996 /**
12997  * Removed an item from the collection.
12998  * @param {Object} o The item to remove.
12999  * @return {Object} The item removed.
13000  */
13001     remove : function(o){
13002         return this.removeAt(this.indexOf(o));
13003     },
13004    
13005 /**
13006  * Remove an item from a specified index in the collection.
13007  * @param {Number} index The index within the collection of the item to remove.
13008  */
13009     removeAt : function(index){
13010         if(index < this.length && index >= 0){
13011             this.length--;
13012             var o = this.items[index];
13013             this.items.splice(index, 1);
13014             var key = this.keys[index];
13015             if(typeof key != "undefined"){
13016                 delete this.map[key];
13017             }
13018             this.keys.splice(index, 1);
13019             this.fireEvent("remove", o, key);
13020         }
13021     },
13022    
13023 /**
13024  * Removed an item associated with the passed key fom the collection.
13025  * @param {String} key The key of the item to remove.
13026  */
13027     removeKey : function(key){
13028         return this.removeAt(this.indexOfKey(key));
13029     },
13030    
13031 /**
13032  * Returns the number of items in the collection.
13033  * @return {Number} the number of items in the collection.
13034  */
13035     getCount : function(){
13036         return this.length; 
13037     },
13038    
13039 /**
13040  * Returns index within the collection of the passed Object.
13041  * @param {Object} o The item to find the index of.
13042  * @return {Number} index of the item.
13043  */
13044     indexOf : function(o){
13045         if(!this.items.indexOf){
13046             for(var i = 0, len = this.items.length; i < len; i++){
13047                 if(this.items[i] == o) return i;
13048             }
13049             return -1;
13050         }else{
13051             return this.items.indexOf(o);
13052         }
13053     },
13054    
13055 /**
13056  * Returns index within the collection of the passed key.
13057  * @param {String} key The key to find the index of.
13058  * @return {Number} index of the key.
13059  */
13060     indexOfKey : function(key){
13061         if(!this.keys.indexOf){
13062             for(var i = 0, len = this.keys.length; i < len; i++){
13063                 if(this.keys[i] == key) return i;
13064             }
13065             return -1;
13066         }else{
13067             return this.keys.indexOf(key);
13068         }
13069     },
13070    
13071 /**
13072  * Returns the item associated with the passed key OR index. Key has priority over index.
13073  * @param {String/Number} key The key or index of the item.
13074  * @return {Object} The item associated with the passed key.
13075  */
13076     item : function(key){
13077         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13078         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13079     },
13080     
13081 /**
13082  * Returns the item at the specified index.
13083  * @param {Number} index The index of the item.
13084  * @return {Object}
13085  */
13086     itemAt : function(index){
13087         return this.items[index];
13088     },
13089     
13090 /**
13091  * Returns the item associated with the passed key.
13092  * @param {String/Number} key The key of the item.
13093  * @return {Object} The item associated with the passed key.
13094  */
13095     key : function(key){
13096         return this.map[key];
13097     },
13098    
13099 /**
13100  * Returns true if the collection contains the passed Object as an item.
13101  * @param {Object} o  The Object to look for in the collection.
13102  * @return {Boolean} True if the collection contains the Object as an item.
13103  */
13104     contains : function(o){
13105         return this.indexOf(o) != -1;
13106     },
13107    
13108 /**
13109  * Returns true if the collection contains the passed Object as a key.
13110  * @param {String} key The key to look for in the collection.
13111  * @return {Boolean} True if the collection contains the Object as a key.
13112  */
13113     containsKey : function(key){
13114         return typeof this.map[key] != "undefined";
13115     },
13116    
13117 /**
13118  * Removes all items from the collection.
13119  */
13120     clear : function(){
13121         this.length = 0;
13122         this.items = [];
13123         this.keys = [];
13124         this.map = {};
13125         this.fireEvent("clear");
13126     },
13127    
13128 /**
13129  * Returns the first item in the collection.
13130  * @return {Object} the first item in the collection..
13131  */
13132     first : function(){
13133         return this.items[0]; 
13134     },
13135    
13136 /**
13137  * Returns the last item in the collection.
13138  * @return {Object} the last item in the collection..
13139  */
13140     last : function(){
13141         return this.items[this.length-1];   
13142     },
13143     
13144     _sort : function(property, dir, fn){
13145         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13146         fn = fn || function(a, b){
13147             return a-b;
13148         };
13149         var c = [], k = this.keys, items = this.items;
13150         for(var i = 0, len = items.length; i < len; i++){
13151             c[c.length] = {key: k[i], value: items[i], index: i};
13152         }
13153         c.sort(function(a, b){
13154             var v = fn(a[property], b[property]) * dsc;
13155             if(v == 0){
13156                 v = (a.index < b.index ? -1 : 1);
13157             }
13158             return v;
13159         });
13160         for(var i = 0, len = c.length; i < len; i++){
13161             items[i] = c[i].value;
13162             k[i] = c[i].key;
13163         }
13164         this.fireEvent("sort", this);
13165     },
13166     
13167     /**
13168      * Sorts this collection with the passed comparison function
13169      * @param {String} direction (optional) "ASC" or "DESC"
13170      * @param {Function} fn (optional) comparison function
13171      */
13172     sort : function(dir, fn){
13173         this._sort("value", dir, fn);
13174     },
13175     
13176     /**
13177      * Sorts this collection by keys
13178      * @param {String} direction (optional) "ASC" or "DESC"
13179      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13180      */
13181     keySort : function(dir, fn){
13182         this._sort("key", dir, fn || function(a, b){
13183             return String(a).toUpperCase()-String(b).toUpperCase();
13184         });
13185     },
13186     
13187     /**
13188      * Returns a range of items in this collection
13189      * @param {Number} startIndex (optional) defaults to 0
13190      * @param {Number} endIndex (optional) default to the last item
13191      * @return {Array} An array of items
13192      */
13193     getRange : function(start, end){
13194         var items = this.items;
13195         if(items.length < 1){
13196             return [];
13197         }
13198         start = start || 0;
13199         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13200         var r = [];
13201         if(start <= end){
13202             for(var i = start; i <= end; i++) {
13203                     r[r.length] = items[i];
13204             }
13205         }else{
13206             for(var i = start; i >= end; i--) {
13207                     r[r.length] = items[i];
13208             }
13209         }
13210         return r;
13211     },
13212         
13213     /**
13214      * Filter the <i>objects</i> in this collection by a specific property. 
13215      * Returns a new collection that has been filtered.
13216      * @param {String} property A property on your objects
13217      * @param {String/RegExp} value Either string that the property values 
13218      * should start with or a RegExp to test against the property
13219      * @return {MixedCollection} The new filtered collection
13220      */
13221     filter : function(property, value){
13222         if(!value.exec){ // not a regex
13223             value = String(value);
13224             if(value.length == 0){
13225                 return this.clone();
13226             }
13227             value = new RegExp("^" + Roo.escapeRe(value), "i");
13228         }
13229         return this.filterBy(function(o){
13230             return o && value.test(o[property]);
13231         });
13232         },
13233     
13234     /**
13235      * Filter by a function. * Returns a new collection that has been filtered.
13236      * The passed function will be called with each 
13237      * object in the collection. If the function returns true, the value is included 
13238      * otherwise it is filtered.
13239      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13240      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13241      * @return {MixedCollection} The new filtered collection
13242      */
13243     filterBy : function(fn, scope){
13244         var r = new Roo.util.MixedCollection();
13245         r.getKey = this.getKey;
13246         var k = this.keys, it = this.items;
13247         for(var i = 0, len = it.length; i < len; i++){
13248             if(fn.call(scope||this, it[i], k[i])){
13249                                 r.add(k[i], it[i]);
13250                         }
13251         }
13252         return r;
13253     },
13254     
13255     /**
13256      * Creates a duplicate of this collection
13257      * @return {MixedCollection}
13258      */
13259     clone : function(){
13260         var r = new Roo.util.MixedCollection();
13261         var k = this.keys, it = this.items;
13262         for(var i = 0, len = it.length; i < len; i++){
13263             r.add(k[i], it[i]);
13264         }
13265         r.getKey = this.getKey;
13266         return r;
13267     }
13268 });
13269 /**
13270  * Returns the item associated with the passed key or index.
13271  * @method
13272  * @param {String/Number} key The key or index of the item.
13273  * @return {Object} The item associated with the passed key.
13274  */
13275 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13276  * Based on:
13277  * Ext JS Library 1.1.1
13278  * Copyright(c) 2006-2007, Ext JS, LLC.
13279  *
13280  * Originally Released Under LGPL - original licence link has changed is not relivant.
13281  *
13282  * Fork - LGPL
13283  * <script type="text/javascript">
13284  */
13285 /**
13286  * @class Roo.util.JSON
13287  * Modified version of Douglas Crockford"s json.js that doesn"t
13288  * mess with the Object prototype 
13289  * http://www.json.org/js.html
13290  * @singleton
13291  */
13292 Roo.util.JSON = new (function(){
13293     var useHasOwn = {}.hasOwnProperty ? true : false;
13294     
13295     // crashes Safari in some instances
13296     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13297     
13298     var pad = function(n) {
13299         return n < 10 ? "0" + n : n;
13300     };
13301     
13302     var m = {
13303         "\b": '\\b',
13304         "\t": '\\t',
13305         "\n": '\\n',
13306         "\f": '\\f',
13307         "\r": '\\r',
13308         '"' : '\\"',
13309         "\\": '\\\\'
13310     };
13311
13312     var encodeString = function(s){
13313         if (/["\\\x00-\x1f]/.test(s)) {
13314             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13315                 var c = m[b];
13316                 if(c){
13317                     return c;
13318                 }
13319                 c = b.charCodeAt();
13320                 return "\\u00" +
13321                     Math.floor(c / 16).toString(16) +
13322                     (c % 16).toString(16);
13323             }) + '"';
13324         }
13325         return '"' + s + '"';
13326     };
13327     
13328     var encodeArray = function(o){
13329         var a = ["["], b, i, l = o.length, v;
13330             for (i = 0; i < l; i += 1) {
13331                 v = o[i];
13332                 switch (typeof v) {
13333                     case "undefined":
13334                     case "function":
13335                     case "unknown":
13336                         break;
13337                     default:
13338                         if (b) {
13339                             a.push(',');
13340                         }
13341                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13342                         b = true;
13343                 }
13344             }
13345             a.push("]");
13346             return a.join("");
13347     };
13348     
13349     var encodeDate = function(o){
13350         return '"' + o.getFullYear() + "-" +
13351                 pad(o.getMonth() + 1) + "-" +
13352                 pad(o.getDate()) + "T" +
13353                 pad(o.getHours()) + ":" +
13354                 pad(o.getMinutes()) + ":" +
13355                 pad(o.getSeconds()) + '"';
13356     };
13357     
13358     /**
13359      * Encodes an Object, Array or other value
13360      * @param {Mixed} o The variable to encode
13361      * @return {String} The JSON string
13362      */
13363     this.encode = function(o)
13364     {
13365         // should this be extended to fully wrap stringify..
13366         
13367         if(typeof o == "undefined" || o === null){
13368             return "null";
13369         }else if(o instanceof Array){
13370             return encodeArray(o);
13371         }else if(o instanceof Date){
13372             return encodeDate(o);
13373         }else if(typeof o == "string"){
13374             return encodeString(o);
13375         }else if(typeof o == "number"){
13376             return isFinite(o) ? String(o) : "null";
13377         }else if(typeof o == "boolean"){
13378             return String(o);
13379         }else {
13380             var a = ["{"], b, i, v;
13381             for (i in o) {
13382                 if(!useHasOwn || o.hasOwnProperty(i)) {
13383                     v = o[i];
13384                     switch (typeof v) {
13385                     case "undefined":
13386                     case "function":
13387                     case "unknown":
13388                         break;
13389                     default:
13390                         if(b){
13391                             a.push(',');
13392                         }
13393                         a.push(this.encode(i), ":",
13394                                 v === null ? "null" : this.encode(v));
13395                         b = true;
13396                     }
13397                 }
13398             }
13399             a.push("}");
13400             return a.join("");
13401         }
13402     };
13403     
13404     /**
13405      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13406      * @param {String} json The JSON string
13407      * @return {Object} The resulting object
13408      */
13409     this.decode = function(json){
13410         
13411         return  /** eval:var:json */ eval("(" + json + ')');
13412     };
13413 })();
13414 /** 
13415  * Shorthand for {@link Roo.util.JSON#encode}
13416  * @member Roo encode 
13417  * @method */
13418 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13419 /** 
13420  * Shorthand for {@link Roo.util.JSON#decode}
13421  * @member Roo decode 
13422  * @method */
13423 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13424 /*
13425  * Based on:
13426  * Ext JS Library 1.1.1
13427  * Copyright(c) 2006-2007, Ext JS, LLC.
13428  *
13429  * Originally Released Under LGPL - original licence link has changed is not relivant.
13430  *
13431  * Fork - LGPL
13432  * <script type="text/javascript">
13433  */
13434  
13435 /**
13436  * @class Roo.util.Format
13437  * Reusable data formatting functions
13438  * @singleton
13439  */
13440 Roo.util.Format = function(){
13441     var trimRe = /^\s+|\s+$/g;
13442     return {
13443         /**
13444          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13445          * @param {String} value The string to truncate
13446          * @param {Number} length The maximum length to allow before truncating
13447          * @return {String} The converted text
13448          */
13449         ellipsis : function(value, len){
13450             if(value && value.length > len){
13451                 return value.substr(0, len-3)+"...";
13452             }
13453             return value;
13454         },
13455
13456         /**
13457          * Checks a reference and converts it to empty string if it is undefined
13458          * @param {Mixed} value Reference to check
13459          * @return {Mixed} Empty string if converted, otherwise the original value
13460          */
13461         undef : function(value){
13462             return typeof value != "undefined" ? value : "";
13463         },
13464
13465         /**
13466          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13467          * @param {String} value The string to encode
13468          * @return {String} The encoded text
13469          */
13470         htmlEncode : function(value){
13471             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13472         },
13473
13474         /**
13475          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13476          * @param {String} value The string to decode
13477          * @return {String} The decoded text
13478          */
13479         htmlDecode : function(value){
13480             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13481         },
13482
13483         /**
13484          * Trims any whitespace from either side of a string
13485          * @param {String} value The text to trim
13486          * @return {String} The trimmed text
13487          */
13488         trim : function(value){
13489             return String(value).replace(trimRe, "");
13490         },
13491
13492         /**
13493          * Returns a substring from within an original string
13494          * @param {String} value The original text
13495          * @param {Number} start The start index of the substring
13496          * @param {Number} length The length of the substring
13497          * @return {String} The substring
13498          */
13499         substr : function(value, start, length){
13500             return String(value).substr(start, length);
13501         },
13502
13503         /**
13504          * Converts a string to all lower case letters
13505          * @param {String} value The text to convert
13506          * @return {String} The converted text
13507          */
13508         lowercase : function(value){
13509             return String(value).toLowerCase();
13510         },
13511
13512         /**
13513          * Converts a string to all upper case letters
13514          * @param {String} value The text to convert
13515          * @return {String} The converted text
13516          */
13517         uppercase : function(value){
13518             return String(value).toUpperCase();
13519         },
13520
13521         /**
13522          * Converts the first character only of a string to upper case
13523          * @param {String} value The text to convert
13524          * @return {String} The converted text
13525          */
13526         capitalize : function(value){
13527             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13528         },
13529
13530         // private
13531         call : function(value, fn){
13532             if(arguments.length > 2){
13533                 var args = Array.prototype.slice.call(arguments, 2);
13534                 args.unshift(value);
13535                  
13536                 return /** eval:var:value */  eval(fn).apply(window, args);
13537             }else{
13538                 /** eval:var:value */
13539                 return /** eval:var:value */ eval(fn).call(window, value);
13540             }
13541         },
13542
13543        
13544         /**
13545          * safer version of Math.toFixed..??/
13546          * @param {Number/String} value The numeric value to format
13547          * @param {Number/String} value Decimal places 
13548          * @return {String} The formatted currency string
13549          */
13550         toFixed : function(v, n)
13551         {
13552             // why not use to fixed - precision is buggered???
13553             if (!n) {
13554                 return Math.round(v-0);
13555             }
13556             var fact = Math.pow(10,n+1);
13557             v = (Math.round((v-0)*fact))/fact;
13558             var z = (''+fact).substring(2);
13559             if (v == Math.floor(v)) {
13560                 return Math.floor(v) + '.' + z;
13561             }
13562             
13563             // now just padd decimals..
13564             var ps = String(v).split('.');
13565             var fd = (ps[1] + z);
13566             var r = fd.substring(0,n); 
13567             var rm = fd.substring(n); 
13568             if (rm < 5) {
13569                 return ps[0] + '.' + r;
13570             }
13571             r*=1; // turn it into a number;
13572             r++;
13573             if (String(r).length != n) {
13574                 ps[0]*=1;
13575                 ps[0]++;
13576                 r = String(r).substring(1); // chop the end off.
13577             }
13578             
13579             return ps[0] + '.' + r;
13580              
13581         },
13582         
13583         /**
13584          * Format a number as US currency
13585          * @param {Number/String} value The numeric value to format
13586          * @return {String} The formatted currency string
13587          */
13588         usMoney : function(v){
13589             return '$' + Roo.util.Format.number(v);
13590         },
13591         
13592         /**
13593          * Format a number
13594          * eventually this should probably emulate php's number_format
13595          * @param {Number/String} value The numeric value to format
13596          * @param {Number} decimals number of decimal places
13597          * @return {String} The formatted currency string
13598          */
13599         number : function(v,decimals)
13600         {
13601             // multiply and round.
13602             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13603             var mul = Math.pow(10, decimals);
13604             var zero = String(mul).substring(1);
13605             v = (Math.round((v-0)*mul))/mul;
13606             
13607             // if it's '0' number.. then
13608             
13609             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13610             v = String(v);
13611             var ps = v.split('.');
13612             var whole = ps[0];
13613             
13614             
13615             var r = /(\d+)(\d{3})/;
13616             // add comma's
13617             while (r.test(whole)) {
13618                 whole = whole.replace(r, '$1' + ',' + '$2');
13619             }
13620             
13621             
13622             var sub = ps[1] ?
13623                     // has decimals..
13624                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13625                     // does not have decimals
13626                     (decimals ? ('.' + zero) : '');
13627             
13628             
13629             return whole + sub ;
13630         },
13631         
13632         /**
13633          * Parse a value into a formatted date using the specified format pattern.
13634          * @param {Mixed} value The value to format
13635          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13636          * @return {String} The formatted date string
13637          */
13638         date : function(v, format){
13639             if(!v){
13640                 return "";
13641             }
13642             if(!(v instanceof Date)){
13643                 v = new Date(Date.parse(v));
13644             }
13645             return v.dateFormat(format || "m/d/Y");
13646         },
13647
13648         /**
13649          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13650          * @param {String} format Any valid date format string
13651          * @return {Function} The date formatting function
13652          */
13653         dateRenderer : function(format){
13654             return function(v){
13655                 return Roo.util.Format.date(v, format);  
13656             };
13657         },
13658
13659         // private
13660         stripTagsRE : /<\/?[^>]+>/gi,
13661         
13662         /**
13663          * Strips all HTML tags
13664          * @param {Mixed} value The text from which to strip tags
13665          * @return {String} The stripped text
13666          */
13667         stripTags : function(v){
13668             return !v ? v : String(v).replace(this.stripTagsRE, "");
13669         }
13670     };
13671 }();/*
13672  * Based on:
13673  * Ext JS Library 1.1.1
13674  * Copyright(c) 2006-2007, Ext JS, LLC.
13675  *
13676  * Originally Released Under LGPL - original licence link has changed is not relivant.
13677  *
13678  * Fork - LGPL
13679  * <script type="text/javascript">
13680  */
13681
13682
13683  
13684
13685 /**
13686  * @class Roo.MasterTemplate
13687  * @extends Roo.Template
13688  * Provides a template that can have child templates. The syntax is:
13689 <pre><code>
13690 var t = new Roo.MasterTemplate(
13691         '&lt;select name="{name}"&gt;',
13692                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13693         '&lt;/select&gt;'
13694 );
13695 t.add('options', {value: 'foo', text: 'bar'});
13696 // or you can add multiple child elements in one shot
13697 t.addAll('options', [
13698     {value: 'foo', text: 'bar'},
13699     {value: 'foo2', text: 'bar2'},
13700     {value: 'foo3', text: 'bar3'}
13701 ]);
13702 // then append, applying the master template values
13703 t.append('my-form', {name: 'my-select'});
13704 </code></pre>
13705 * A name attribute for the child template is not required if you have only one child
13706 * template or you want to refer to them by index.
13707  */
13708 Roo.MasterTemplate = function(){
13709     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13710     this.originalHtml = this.html;
13711     var st = {};
13712     var m, re = this.subTemplateRe;
13713     re.lastIndex = 0;
13714     var subIndex = 0;
13715     while(m = re.exec(this.html)){
13716         var name = m[1], content = m[2];
13717         st[subIndex] = {
13718             name: name,
13719             index: subIndex,
13720             buffer: [],
13721             tpl : new Roo.Template(content)
13722         };
13723         if(name){
13724             st[name] = st[subIndex];
13725         }
13726         st[subIndex].tpl.compile();
13727         st[subIndex].tpl.call = this.call.createDelegate(this);
13728         subIndex++;
13729     }
13730     this.subCount = subIndex;
13731     this.subs = st;
13732 };
13733 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13734     /**
13735     * The regular expression used to match sub templates
13736     * @type RegExp
13737     * @property
13738     */
13739     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13740
13741     /**
13742      * Applies the passed values to a child template.
13743      * @param {String/Number} name (optional) The name or index of the child template
13744      * @param {Array/Object} values The values to be applied to the template
13745      * @return {MasterTemplate} this
13746      */
13747      add : function(name, values){
13748         if(arguments.length == 1){
13749             values = arguments[0];
13750             name = 0;
13751         }
13752         var s = this.subs[name];
13753         s.buffer[s.buffer.length] = s.tpl.apply(values);
13754         return this;
13755     },
13756
13757     /**
13758      * Applies all the passed values to a child template.
13759      * @param {String/Number} name (optional) The name or index of the child template
13760      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13761      * @param {Boolean} reset (optional) True to reset the template first
13762      * @return {MasterTemplate} this
13763      */
13764     fill : function(name, values, reset){
13765         var a = arguments;
13766         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13767             values = a[0];
13768             name = 0;
13769             reset = a[1];
13770         }
13771         if(reset){
13772             this.reset();
13773         }
13774         for(var i = 0, len = values.length; i < len; i++){
13775             this.add(name, values[i]);
13776         }
13777         return this;
13778     },
13779
13780     /**
13781      * Resets the template for reuse
13782      * @return {MasterTemplate} this
13783      */
13784      reset : function(){
13785         var s = this.subs;
13786         for(var i = 0; i < this.subCount; i++){
13787             s[i].buffer = [];
13788         }
13789         return this;
13790     },
13791
13792     applyTemplate : function(values){
13793         var s = this.subs;
13794         var replaceIndex = -1;
13795         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13796             return s[++replaceIndex].buffer.join("");
13797         });
13798         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13799     },
13800
13801     apply : function(){
13802         return this.applyTemplate.apply(this, arguments);
13803     },
13804
13805     compile : function(){return this;}
13806 });
13807
13808 /**
13809  * Alias for fill().
13810  * @method
13811  */
13812 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13813  /**
13814  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13815  * var tpl = Roo.MasterTemplate.from('element-id');
13816  * @param {String/HTMLElement} el
13817  * @param {Object} config
13818  * @static
13819  */
13820 Roo.MasterTemplate.from = function(el, config){
13821     el = Roo.getDom(el);
13822     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13823 };/*
13824  * Based on:
13825  * Ext JS Library 1.1.1
13826  * Copyright(c) 2006-2007, Ext JS, LLC.
13827  *
13828  * Originally Released Under LGPL - original licence link has changed is not relivant.
13829  *
13830  * Fork - LGPL
13831  * <script type="text/javascript">
13832  */
13833
13834  
13835 /**
13836  * @class Roo.util.CSS
13837  * Utility class for manipulating CSS rules
13838  * @singleton
13839  */
13840 Roo.util.CSS = function(){
13841         var rules = null;
13842         var doc = document;
13843
13844     var camelRe = /(-[a-z])/gi;
13845     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13846
13847    return {
13848    /**
13849     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13850     * tag and appended to the HEAD of the document.
13851     * @param {String|Object} cssText The text containing the css rules
13852     * @param {String} id An id to add to the stylesheet for later removal
13853     * @return {StyleSheet}
13854     */
13855     createStyleSheet : function(cssText, id){
13856         var ss;
13857         var head = doc.getElementsByTagName("head")[0];
13858         var nrules = doc.createElement("style");
13859         nrules.setAttribute("type", "text/css");
13860         if(id){
13861             nrules.setAttribute("id", id);
13862         }
13863         if (typeof(cssText) != 'string') {
13864             // support object maps..
13865             // not sure if this a good idea.. 
13866             // perhaps it should be merged with the general css handling
13867             // and handle js style props.
13868             var cssTextNew = [];
13869             for(var n in cssText) {
13870                 var citems = [];
13871                 for(var k in cssText[n]) {
13872                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13873                 }
13874                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13875                 
13876             }
13877             cssText = cssTextNew.join("\n");
13878             
13879         }
13880        
13881        
13882        if(Roo.isIE){
13883            head.appendChild(nrules);
13884            ss = nrules.styleSheet;
13885            ss.cssText = cssText;
13886        }else{
13887            try{
13888                 nrules.appendChild(doc.createTextNode(cssText));
13889            }catch(e){
13890                nrules.cssText = cssText; 
13891            }
13892            head.appendChild(nrules);
13893            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13894        }
13895        this.cacheStyleSheet(ss);
13896        return ss;
13897    },
13898
13899    /**
13900     * Removes a style or link tag by id
13901     * @param {String} id The id of the tag
13902     */
13903    removeStyleSheet : function(id){
13904        var existing = doc.getElementById(id);
13905        if(existing){
13906            existing.parentNode.removeChild(existing);
13907        }
13908    },
13909
13910    /**
13911     * Dynamically swaps an existing stylesheet reference for a new one
13912     * @param {String} id The id of an existing link tag to remove
13913     * @param {String} url The href of the new stylesheet to include
13914     */
13915    swapStyleSheet : function(id, url){
13916        this.removeStyleSheet(id);
13917        var ss = doc.createElement("link");
13918        ss.setAttribute("rel", "stylesheet");
13919        ss.setAttribute("type", "text/css");
13920        ss.setAttribute("id", id);
13921        ss.setAttribute("href", url);
13922        doc.getElementsByTagName("head")[0].appendChild(ss);
13923    },
13924    
13925    /**
13926     * Refresh the rule cache if you have dynamically added stylesheets
13927     * @return {Object} An object (hash) of rules indexed by selector
13928     */
13929    refreshCache : function(){
13930        return this.getRules(true);
13931    },
13932
13933    // private
13934    cacheStyleSheet : function(stylesheet){
13935        if(!rules){
13936            rules = {};
13937        }
13938        try{// try catch for cross domain access issue
13939            var ssRules = stylesheet.cssRules || stylesheet.rules;
13940            for(var j = ssRules.length-1; j >= 0; --j){
13941                rules[ssRules[j].selectorText] = ssRules[j];
13942            }
13943        }catch(e){}
13944    },
13945    
13946    /**
13947     * Gets all css rules for the document
13948     * @param {Boolean} refreshCache true to refresh the internal cache
13949     * @return {Object} An object (hash) of rules indexed by selector
13950     */
13951    getRules : function(refreshCache){
13952                 if(rules == null || refreshCache){
13953                         rules = {};
13954                         var ds = doc.styleSheets;
13955                         for(var i =0, len = ds.length; i < len; i++){
13956                             try{
13957                         this.cacheStyleSheet(ds[i]);
13958                     }catch(e){} 
13959                 }
13960                 }
13961                 return rules;
13962         },
13963         
13964         /**
13965     * Gets an an individual CSS rule by selector(s)
13966     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13967     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13968     * @return {CSSRule} The CSS rule or null if one is not found
13969     */
13970    getRule : function(selector, refreshCache){
13971                 var rs = this.getRules(refreshCache);
13972                 if(!(selector instanceof Array)){
13973                     return rs[selector];
13974                 }
13975                 for(var i = 0; i < selector.length; i++){
13976                         if(rs[selector[i]]){
13977                                 return rs[selector[i]];
13978                         }
13979                 }
13980                 return null;
13981         },
13982         
13983         
13984         /**
13985     * Updates a rule property
13986     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13987     * @param {String} property The css property
13988     * @param {String} value The new value for the property
13989     * @return {Boolean} true If a rule was found and updated
13990     */
13991    updateRule : function(selector, property, value){
13992                 if(!(selector instanceof Array)){
13993                         var rule = this.getRule(selector);
13994                         if(rule){
13995                                 rule.style[property.replace(camelRe, camelFn)] = value;
13996                                 return true;
13997                         }
13998                 }else{
13999                         for(var i = 0; i < selector.length; i++){
14000                                 if(this.updateRule(selector[i], property, value)){
14001                                         return true;
14002                                 }
14003                         }
14004                 }
14005                 return false;
14006         }
14007    };   
14008 }();/*
14009  * Based on:
14010  * Ext JS Library 1.1.1
14011  * Copyright(c) 2006-2007, Ext JS, LLC.
14012  *
14013  * Originally Released Under LGPL - original licence link has changed is not relivant.
14014  *
14015  * Fork - LGPL
14016  * <script type="text/javascript">
14017  */
14018
14019  
14020
14021 /**
14022  * @class Roo.util.ClickRepeater
14023  * @extends Roo.util.Observable
14024  * 
14025  * A wrapper class which can be applied to any element. Fires a "click" event while the
14026  * mouse is pressed. The interval between firings may be specified in the config but
14027  * defaults to 10 milliseconds.
14028  * 
14029  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14030  * 
14031  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14032  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14033  * Similar to an autorepeat key delay.
14034  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14035  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14036  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14037  *           "interval" and "delay" are ignored. "immediate" is honored.
14038  * @cfg {Boolean} preventDefault True to prevent the default click event
14039  * @cfg {Boolean} stopDefault True to stop the default click event
14040  * 
14041  * @history
14042  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14043  *     2007-02-02 jvs Renamed to ClickRepeater
14044  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14045  *
14046  *  @constructor
14047  * @param {String/HTMLElement/Element} el The element to listen on
14048  * @param {Object} config
14049  **/
14050 Roo.util.ClickRepeater = function(el, config)
14051 {
14052     this.el = Roo.get(el);
14053     this.el.unselectable();
14054
14055     Roo.apply(this, config);
14056
14057     this.addEvents({
14058     /**
14059      * @event mousedown
14060      * Fires when the mouse button is depressed.
14061      * @param {Roo.util.ClickRepeater} this
14062      */
14063         "mousedown" : true,
14064     /**
14065      * @event click
14066      * Fires on a specified interval during the time the element is pressed.
14067      * @param {Roo.util.ClickRepeater} this
14068      */
14069         "click" : true,
14070     /**
14071      * @event mouseup
14072      * Fires when the mouse key is released.
14073      * @param {Roo.util.ClickRepeater} this
14074      */
14075         "mouseup" : true
14076     });
14077
14078     this.el.on("mousedown", this.handleMouseDown, this);
14079     if(this.preventDefault || this.stopDefault){
14080         this.el.on("click", function(e){
14081             if(this.preventDefault){
14082                 e.preventDefault();
14083             }
14084             if(this.stopDefault){
14085                 e.stopEvent();
14086             }
14087         }, this);
14088     }
14089
14090     // allow inline handler
14091     if(this.handler){
14092         this.on("click", this.handler,  this.scope || this);
14093     }
14094
14095     Roo.util.ClickRepeater.superclass.constructor.call(this);
14096 };
14097
14098 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14099     interval : 20,
14100     delay: 250,
14101     preventDefault : true,
14102     stopDefault : false,
14103     timer : 0,
14104
14105     // private
14106     handleMouseDown : function(){
14107         clearTimeout(this.timer);
14108         this.el.blur();
14109         if(this.pressClass){
14110             this.el.addClass(this.pressClass);
14111         }
14112         this.mousedownTime = new Date();
14113
14114         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14115         this.el.on("mouseout", this.handleMouseOut, this);
14116
14117         this.fireEvent("mousedown", this);
14118         this.fireEvent("click", this);
14119         
14120         this.timer = this.click.defer(this.delay || this.interval, this);
14121     },
14122
14123     // private
14124     click : function(){
14125         this.fireEvent("click", this);
14126         this.timer = this.click.defer(this.getInterval(), this);
14127     },
14128
14129     // private
14130     getInterval: function(){
14131         if(!this.accelerate){
14132             return this.interval;
14133         }
14134         var pressTime = this.mousedownTime.getElapsed();
14135         if(pressTime < 500){
14136             return 400;
14137         }else if(pressTime < 1700){
14138             return 320;
14139         }else if(pressTime < 2600){
14140             return 250;
14141         }else if(pressTime < 3500){
14142             return 180;
14143         }else if(pressTime < 4400){
14144             return 140;
14145         }else if(pressTime < 5300){
14146             return 80;
14147         }else if(pressTime < 6200){
14148             return 50;
14149         }else{
14150             return 10;
14151         }
14152     },
14153
14154     // private
14155     handleMouseOut : function(){
14156         clearTimeout(this.timer);
14157         if(this.pressClass){
14158             this.el.removeClass(this.pressClass);
14159         }
14160         this.el.on("mouseover", this.handleMouseReturn, this);
14161     },
14162
14163     // private
14164     handleMouseReturn : function(){
14165         this.el.un("mouseover", this.handleMouseReturn);
14166         if(this.pressClass){
14167             this.el.addClass(this.pressClass);
14168         }
14169         this.click();
14170     },
14171
14172     // private
14173     handleMouseUp : function(){
14174         clearTimeout(this.timer);
14175         this.el.un("mouseover", this.handleMouseReturn);
14176         this.el.un("mouseout", this.handleMouseOut);
14177         Roo.get(document).un("mouseup", this.handleMouseUp);
14178         this.el.removeClass(this.pressClass);
14179         this.fireEvent("mouseup", this);
14180     }
14181 });/*
14182  * Based on:
14183  * Ext JS Library 1.1.1
14184  * Copyright(c) 2006-2007, Ext JS, LLC.
14185  *
14186  * Originally Released Under LGPL - original licence link has changed is not relivant.
14187  *
14188  * Fork - LGPL
14189  * <script type="text/javascript">
14190  */
14191
14192  
14193 /**
14194  * @class Roo.KeyNav
14195  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14196  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14197  * way to implement custom navigation schemes for any UI component.</p>
14198  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14199  * pageUp, pageDown, del, home, end.  Usage:</p>
14200  <pre><code>
14201 var nav = new Roo.KeyNav("my-element", {
14202     "left" : function(e){
14203         this.moveLeft(e.ctrlKey);
14204     },
14205     "right" : function(e){
14206         this.moveRight(e.ctrlKey);
14207     },
14208     "enter" : function(e){
14209         this.save();
14210     },
14211     scope : this
14212 });
14213 </code></pre>
14214  * @constructor
14215  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14216  * @param {Object} config The config
14217  */
14218 Roo.KeyNav = function(el, config){
14219     this.el = Roo.get(el);
14220     Roo.apply(this, config);
14221     if(!this.disabled){
14222         this.disabled = true;
14223         this.enable();
14224     }
14225 };
14226
14227 Roo.KeyNav.prototype = {
14228     /**
14229      * @cfg {Boolean} disabled
14230      * True to disable this KeyNav instance (defaults to false)
14231      */
14232     disabled : false,
14233     /**
14234      * @cfg {String} defaultEventAction
14235      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14236      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14237      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14238      */
14239     defaultEventAction: "stopEvent",
14240     /**
14241      * @cfg {Boolean} forceKeyDown
14242      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14243      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14244      * handle keydown instead of keypress.
14245      */
14246     forceKeyDown : false,
14247
14248     // private
14249     prepareEvent : function(e){
14250         var k = e.getKey();
14251         var h = this.keyToHandler[k];
14252         //if(h && this[h]){
14253         //    e.stopPropagation();
14254         //}
14255         if(Roo.isSafari && h && k >= 37 && k <= 40){
14256             e.stopEvent();
14257         }
14258     },
14259
14260     // private
14261     relay : function(e){
14262         var k = e.getKey();
14263         var h = this.keyToHandler[k];
14264         if(h && this[h]){
14265             if(this.doRelay(e, this[h], h) !== true){
14266                 e[this.defaultEventAction]();
14267             }
14268         }
14269     },
14270
14271     // private
14272     doRelay : function(e, h, hname){
14273         return h.call(this.scope || this, e);
14274     },
14275
14276     // possible handlers
14277     enter : false,
14278     left : false,
14279     right : false,
14280     up : false,
14281     down : false,
14282     tab : false,
14283     esc : false,
14284     pageUp : false,
14285     pageDown : false,
14286     del : false,
14287     home : false,
14288     end : false,
14289
14290     // quick lookup hash
14291     keyToHandler : {
14292         37 : "left",
14293         39 : "right",
14294         38 : "up",
14295         40 : "down",
14296         33 : "pageUp",
14297         34 : "pageDown",
14298         46 : "del",
14299         36 : "home",
14300         35 : "end",
14301         13 : "enter",
14302         27 : "esc",
14303         9  : "tab"
14304     },
14305
14306         /**
14307          * Enable this KeyNav
14308          */
14309         enable: function(){
14310                 if(this.disabled){
14311             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14312             // the EventObject will normalize Safari automatically
14313             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14314                 this.el.on("keydown", this.relay,  this);
14315             }else{
14316                 this.el.on("keydown", this.prepareEvent,  this);
14317                 this.el.on("keypress", this.relay,  this);
14318             }
14319                     this.disabled = false;
14320                 }
14321         },
14322
14323         /**
14324          * Disable this KeyNav
14325          */
14326         disable: function(){
14327                 if(!this.disabled){
14328                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14329                 this.el.un("keydown", this.relay);
14330             }else{
14331                 this.el.un("keydown", this.prepareEvent);
14332                 this.el.un("keypress", this.relay);
14333             }
14334                     this.disabled = true;
14335                 }
14336         }
14337 };/*
14338  * Based on:
14339  * Ext JS Library 1.1.1
14340  * Copyright(c) 2006-2007, Ext JS, LLC.
14341  *
14342  * Originally Released Under LGPL - original licence link has changed is not relivant.
14343  *
14344  * Fork - LGPL
14345  * <script type="text/javascript">
14346  */
14347
14348  
14349 /**
14350  * @class Roo.KeyMap
14351  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14352  * The constructor accepts the same config object as defined by {@link #addBinding}.
14353  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14354  * combination it will call the function with this signature (if the match is a multi-key
14355  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14356  * A KeyMap can also handle a string representation of keys.<br />
14357  * Usage:
14358  <pre><code>
14359 // map one key by key code
14360 var map = new Roo.KeyMap("my-element", {
14361     key: 13, // or Roo.EventObject.ENTER
14362     fn: myHandler,
14363     scope: myObject
14364 });
14365
14366 // map multiple keys to one action by string
14367 var map = new Roo.KeyMap("my-element", {
14368     key: "a\r\n\t",
14369     fn: myHandler,
14370     scope: myObject
14371 });
14372
14373 // map multiple keys to multiple actions by strings and array of codes
14374 var map = new Roo.KeyMap("my-element", [
14375     {
14376         key: [10,13],
14377         fn: function(){ alert("Return was pressed"); }
14378     }, {
14379         key: "abc",
14380         fn: function(){ alert('a, b or c was pressed'); }
14381     }, {
14382         key: "\t",
14383         ctrl:true,
14384         shift:true,
14385         fn: function(){ alert('Control + shift + tab was pressed.'); }
14386     }
14387 ]);
14388 </code></pre>
14389  * <b>Note: A KeyMap starts enabled</b>
14390  * @constructor
14391  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14392  * @param {Object} config The config (see {@link #addBinding})
14393  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14394  */
14395 Roo.KeyMap = function(el, config, eventName){
14396     this.el  = Roo.get(el);
14397     this.eventName = eventName || "keydown";
14398     this.bindings = [];
14399     if(config){
14400         this.addBinding(config);
14401     }
14402     this.enable();
14403 };
14404
14405 Roo.KeyMap.prototype = {
14406     /**
14407      * True to stop the event from bubbling and prevent the default browser action if the
14408      * key was handled by the KeyMap (defaults to false)
14409      * @type Boolean
14410      */
14411     stopEvent : false,
14412
14413     /**
14414      * Add a new binding to this KeyMap. The following config object properties are supported:
14415      * <pre>
14416 Property    Type             Description
14417 ----------  ---------------  ----------------------------------------------------------------------
14418 key         String/Array     A single keycode or an array of keycodes to handle
14419 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14420 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14421 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14422 fn          Function         The function to call when KeyMap finds the expected key combination
14423 scope       Object           The scope of the callback function
14424 </pre>
14425      *
14426      * Usage:
14427      * <pre><code>
14428 // Create a KeyMap
14429 var map = new Roo.KeyMap(document, {
14430     key: Roo.EventObject.ENTER,
14431     fn: handleKey,
14432     scope: this
14433 });
14434
14435 //Add a new binding to the existing KeyMap later
14436 map.addBinding({
14437     key: 'abc',
14438     shift: true,
14439     fn: handleKey,
14440     scope: this
14441 });
14442 </code></pre>
14443      * @param {Object/Array} config A single KeyMap config or an array of configs
14444      */
14445         addBinding : function(config){
14446         if(config instanceof Array){
14447             for(var i = 0, len = config.length; i < len; i++){
14448                 this.addBinding(config[i]);
14449             }
14450             return;
14451         }
14452         var keyCode = config.key,
14453             shift = config.shift, 
14454             ctrl = config.ctrl, 
14455             alt = config.alt,
14456             fn = config.fn,
14457             scope = config.scope;
14458         if(typeof keyCode == "string"){
14459             var ks = [];
14460             var keyString = keyCode.toUpperCase();
14461             for(var j = 0, len = keyString.length; j < len; j++){
14462                 ks.push(keyString.charCodeAt(j));
14463             }
14464             keyCode = ks;
14465         }
14466         var keyArray = keyCode instanceof Array;
14467         var handler = function(e){
14468             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14469                 var k = e.getKey();
14470                 if(keyArray){
14471                     for(var i = 0, len = keyCode.length; i < len; i++){
14472                         if(keyCode[i] == k){
14473                           if(this.stopEvent){
14474                               e.stopEvent();
14475                           }
14476                           fn.call(scope || window, k, e);
14477                           return;
14478                         }
14479                     }
14480                 }else{
14481                     if(k == keyCode){
14482                         if(this.stopEvent){
14483                            e.stopEvent();
14484                         }
14485                         fn.call(scope || window, k, e);
14486                     }
14487                 }
14488             }
14489         };
14490         this.bindings.push(handler);  
14491         },
14492
14493     /**
14494      * Shorthand for adding a single key listener
14495      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14496      * following options:
14497      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14498      * @param {Function} fn The function to call
14499      * @param {Object} scope (optional) The scope of the function
14500      */
14501     on : function(key, fn, scope){
14502         var keyCode, shift, ctrl, alt;
14503         if(typeof key == "object" && !(key instanceof Array)){
14504             keyCode = key.key;
14505             shift = key.shift;
14506             ctrl = key.ctrl;
14507             alt = key.alt;
14508         }else{
14509             keyCode = key;
14510         }
14511         this.addBinding({
14512             key: keyCode,
14513             shift: shift,
14514             ctrl: ctrl,
14515             alt: alt,
14516             fn: fn,
14517             scope: scope
14518         })
14519     },
14520
14521     // private
14522     handleKeyDown : function(e){
14523             if(this.enabled){ //just in case
14524             var b = this.bindings;
14525             for(var i = 0, len = b.length; i < len; i++){
14526                 b[i].call(this, e);
14527             }
14528             }
14529         },
14530         
14531         /**
14532          * Returns true if this KeyMap is enabled
14533          * @return {Boolean} 
14534          */
14535         isEnabled : function(){
14536             return this.enabled;  
14537         },
14538         
14539         /**
14540          * Enables this KeyMap
14541          */
14542         enable: function(){
14543                 if(!this.enabled){
14544                     this.el.on(this.eventName, this.handleKeyDown, this);
14545                     this.enabled = true;
14546                 }
14547         },
14548
14549         /**
14550          * Disable this KeyMap
14551          */
14552         disable: function(){
14553                 if(this.enabled){
14554                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14555                     this.enabled = false;
14556                 }
14557         }
14558 };/*
14559  * Based on:
14560  * Ext JS Library 1.1.1
14561  * Copyright(c) 2006-2007, Ext JS, LLC.
14562  *
14563  * Originally Released Under LGPL - original licence link has changed is not relivant.
14564  *
14565  * Fork - LGPL
14566  * <script type="text/javascript">
14567  */
14568
14569  
14570 /**
14571  * @class Roo.util.TextMetrics
14572  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14573  * wide, in pixels, a given block of text will be.
14574  * @singleton
14575  */
14576 Roo.util.TextMetrics = function(){
14577     var shared;
14578     return {
14579         /**
14580          * Measures the size of the specified text
14581          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14582          * that can affect the size of the rendered text
14583          * @param {String} text The text to measure
14584          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14585          * in order to accurately measure the text height
14586          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14587          */
14588         measure : function(el, text, fixedWidth){
14589             if(!shared){
14590                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14591             }
14592             shared.bind(el);
14593             shared.setFixedWidth(fixedWidth || 'auto');
14594             return shared.getSize(text);
14595         },
14596
14597         /**
14598          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14599          * the overhead of multiple calls to initialize the style properties on each measurement.
14600          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14601          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14602          * in order to accurately measure the text height
14603          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14604          */
14605         createInstance : function(el, fixedWidth){
14606             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14607         }
14608     };
14609 }();
14610
14611  
14612
14613 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14614     var ml = new Roo.Element(document.createElement('div'));
14615     document.body.appendChild(ml.dom);
14616     ml.position('absolute');
14617     ml.setLeftTop(-1000, -1000);
14618     ml.hide();
14619
14620     if(fixedWidth){
14621         ml.setWidth(fixedWidth);
14622     }
14623      
14624     var instance = {
14625         /**
14626          * Returns the size of the specified text based on the internal element's style and width properties
14627          * @memberOf Roo.util.TextMetrics.Instance#
14628          * @param {String} text The text to measure
14629          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14630          */
14631         getSize : function(text){
14632             ml.update(text);
14633             var s = ml.getSize();
14634             ml.update('');
14635             return s;
14636         },
14637
14638         /**
14639          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14640          * that can affect the size of the rendered text
14641          * @memberOf Roo.util.TextMetrics.Instance#
14642          * @param {String/HTMLElement} el The element, dom node or id
14643          */
14644         bind : function(el){
14645             ml.setStyle(
14646                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14647             );
14648         },
14649
14650         /**
14651          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14652          * to set a fixed width in order to accurately measure the text height.
14653          * @memberOf Roo.util.TextMetrics.Instance#
14654          * @param {Number} width The width to set on the element
14655          */
14656         setFixedWidth : function(width){
14657             ml.setWidth(width);
14658         },
14659
14660         /**
14661          * Returns the measured width of the specified text
14662          * @memberOf Roo.util.TextMetrics.Instance#
14663          * @param {String} text The text to measure
14664          * @return {Number} width The width in pixels
14665          */
14666         getWidth : function(text){
14667             ml.dom.style.width = 'auto';
14668             return this.getSize(text).width;
14669         },
14670
14671         /**
14672          * Returns the measured height of the specified text.  For multiline text, be sure to call
14673          * {@link #setFixedWidth} if necessary.
14674          * @memberOf Roo.util.TextMetrics.Instance#
14675          * @param {String} text The text to measure
14676          * @return {Number} height The height in pixels
14677          */
14678         getHeight : function(text){
14679             return this.getSize(text).height;
14680         }
14681     };
14682
14683     instance.bind(bindTo);
14684
14685     return instance;
14686 };
14687
14688 // backwards compat
14689 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14690  * Based on:
14691  * Ext JS Library 1.1.1
14692  * Copyright(c) 2006-2007, Ext JS, LLC.
14693  *
14694  * Originally Released Under LGPL - original licence link has changed is not relivant.
14695  *
14696  * Fork - LGPL
14697  * <script type="text/javascript">
14698  */
14699
14700 /**
14701  * @class Roo.state.Provider
14702  * Abstract base class for state provider implementations. This class provides methods
14703  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14704  * Provider interface.
14705  */
14706 Roo.state.Provider = function(){
14707     /**
14708      * @event statechange
14709      * Fires when a state change occurs.
14710      * @param {Provider} this This state provider
14711      * @param {String} key The state key which was changed
14712      * @param {String} value The encoded value for the state
14713      */
14714     this.addEvents({
14715         "statechange": true
14716     });
14717     this.state = {};
14718     Roo.state.Provider.superclass.constructor.call(this);
14719 };
14720 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14721     /**
14722      * Returns the current value for a key
14723      * @param {String} name The key name
14724      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14725      * @return {Mixed} The state data
14726      */
14727     get : function(name, defaultValue){
14728         return typeof this.state[name] == "undefined" ?
14729             defaultValue : this.state[name];
14730     },
14731     
14732     /**
14733      * Clears a value from the state
14734      * @param {String} name The key name
14735      */
14736     clear : function(name){
14737         delete this.state[name];
14738         this.fireEvent("statechange", this, name, null);
14739     },
14740     
14741     /**
14742      * Sets the value for a key
14743      * @param {String} name The key name
14744      * @param {Mixed} value The value to set
14745      */
14746     set : function(name, value){
14747         this.state[name] = value;
14748         this.fireEvent("statechange", this, name, value);
14749     },
14750     
14751     /**
14752      * Decodes a string previously encoded with {@link #encodeValue}.
14753      * @param {String} value The value to decode
14754      * @return {Mixed} The decoded value
14755      */
14756     decodeValue : function(cookie){
14757         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14758         var matches = re.exec(unescape(cookie));
14759         if(!matches || !matches[1]) return; // non state cookie
14760         var type = matches[1];
14761         var v = matches[2];
14762         switch(type){
14763             case "n":
14764                 return parseFloat(v);
14765             case "d":
14766                 return new Date(Date.parse(v));
14767             case "b":
14768                 return (v == "1");
14769             case "a":
14770                 var all = [];
14771                 var values = v.split("^");
14772                 for(var i = 0, len = values.length; i < len; i++){
14773                     all.push(this.decodeValue(values[i]));
14774                 }
14775                 return all;
14776            case "o":
14777                 var all = {};
14778                 var values = v.split("^");
14779                 for(var i = 0, len = values.length; i < len; i++){
14780                     var kv = values[i].split("=");
14781                     all[kv[0]] = this.decodeValue(kv[1]);
14782                 }
14783                 return all;
14784            default:
14785                 return v;
14786         }
14787     },
14788     
14789     /**
14790      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14791      * @param {Mixed} value The value to encode
14792      * @return {String} The encoded value
14793      */
14794     encodeValue : function(v){
14795         var enc;
14796         if(typeof v == "number"){
14797             enc = "n:" + v;
14798         }else if(typeof v == "boolean"){
14799             enc = "b:" + (v ? "1" : "0");
14800         }else if(v instanceof Date){
14801             enc = "d:" + v.toGMTString();
14802         }else if(v instanceof Array){
14803             var flat = "";
14804             for(var i = 0, len = v.length; i < len; i++){
14805                 flat += this.encodeValue(v[i]);
14806                 if(i != len-1) flat += "^";
14807             }
14808             enc = "a:" + flat;
14809         }else if(typeof v == "object"){
14810             var flat = "";
14811             for(var key in v){
14812                 if(typeof v[key] != "function"){
14813                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14814                 }
14815             }
14816             enc = "o:" + flat.substring(0, flat.length-1);
14817         }else{
14818             enc = "s:" + v;
14819         }
14820         return escape(enc);        
14821     }
14822 });
14823
14824 /*
14825  * Based on:
14826  * Ext JS Library 1.1.1
14827  * Copyright(c) 2006-2007, Ext JS, LLC.
14828  *
14829  * Originally Released Under LGPL - original licence link has changed is not relivant.
14830  *
14831  * Fork - LGPL
14832  * <script type="text/javascript">
14833  */
14834 /**
14835  * @class Roo.state.Manager
14836  * This is the global state manager. By default all components that are "state aware" check this class
14837  * for state information if you don't pass them a custom state provider. In order for this class
14838  * to be useful, it must be initialized with a provider when your application initializes.
14839  <pre><code>
14840 // in your initialization function
14841 init : function(){
14842    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14843    ...
14844    // supposed you have a {@link Roo.BorderLayout}
14845    var layout = new Roo.BorderLayout(...);
14846    layout.restoreState();
14847    // or a {Roo.BasicDialog}
14848    var dialog = new Roo.BasicDialog(...);
14849    dialog.restoreState();
14850  </code></pre>
14851  * @singleton
14852  */
14853 Roo.state.Manager = function(){
14854     var provider = new Roo.state.Provider();
14855     
14856     return {
14857         /**
14858          * Configures the default state provider for your application
14859          * @param {Provider} stateProvider The state provider to set
14860          */
14861         setProvider : function(stateProvider){
14862             provider = stateProvider;
14863         },
14864         
14865         /**
14866          * Returns the current value for a key
14867          * @param {String} name The key name
14868          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14869          * @return {Mixed} The state data
14870          */
14871         get : function(key, defaultValue){
14872             return provider.get(key, defaultValue);
14873         },
14874         
14875         /**
14876          * Sets the value for a key
14877          * @param {String} name The key name
14878          * @param {Mixed} value The state data
14879          */
14880          set : function(key, value){
14881             provider.set(key, value);
14882         },
14883         
14884         /**
14885          * Clears a value from the state
14886          * @param {String} name The key name
14887          */
14888         clear : function(key){
14889             provider.clear(key);
14890         },
14891         
14892         /**
14893          * Gets the currently configured state provider
14894          * @return {Provider} The state provider
14895          */
14896         getProvider : function(){
14897             return provider;
14898         }
14899     };
14900 }();
14901 /*
14902  * Based on:
14903  * Ext JS Library 1.1.1
14904  * Copyright(c) 2006-2007, Ext JS, LLC.
14905  *
14906  * Originally Released Under LGPL - original licence link has changed is not relivant.
14907  *
14908  * Fork - LGPL
14909  * <script type="text/javascript">
14910  */
14911 /**
14912  * @class Roo.state.CookieProvider
14913  * @extends Roo.state.Provider
14914  * The default Provider implementation which saves state via cookies.
14915  * <br />Usage:
14916  <pre><code>
14917    var cp = new Roo.state.CookieProvider({
14918        path: "/cgi-bin/",
14919        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14920        domain: "roojs.com"
14921    })
14922    Roo.state.Manager.setProvider(cp);
14923  </code></pre>
14924  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14925  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14926  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14927  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14928  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14929  * domain the page is running on including the 'www' like 'www.roojs.com')
14930  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14931  * @constructor
14932  * Create a new CookieProvider
14933  * @param {Object} config The configuration object
14934  */
14935 Roo.state.CookieProvider = function(config){
14936     Roo.state.CookieProvider.superclass.constructor.call(this);
14937     this.path = "/";
14938     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14939     this.domain = null;
14940     this.secure = false;
14941     Roo.apply(this, config);
14942     this.state = this.readCookies();
14943 };
14944
14945 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14946     // private
14947     set : function(name, value){
14948         if(typeof value == "undefined" || value === null){
14949             this.clear(name);
14950             return;
14951         }
14952         this.setCookie(name, value);
14953         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14954     },
14955
14956     // private
14957     clear : function(name){
14958         this.clearCookie(name);
14959         Roo.state.CookieProvider.superclass.clear.call(this, name);
14960     },
14961
14962     // private
14963     readCookies : function(){
14964         var cookies = {};
14965         var c = document.cookie + ";";
14966         var re = /\s?(.*?)=(.*?);/g;
14967         var matches;
14968         while((matches = re.exec(c)) != null){
14969             var name = matches[1];
14970             var value = matches[2];
14971             if(name && name.substring(0,3) == "ys-"){
14972                 cookies[name.substr(3)] = this.decodeValue(value);
14973             }
14974         }
14975         return cookies;
14976     },
14977
14978     // private
14979     setCookie : function(name, value){
14980         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14981            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14982            ((this.path == null) ? "" : ("; path=" + this.path)) +
14983            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14984            ((this.secure == true) ? "; secure" : "");
14985     },
14986
14987     // private
14988     clearCookie : function(name){
14989         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14990            ((this.path == null) ? "" : ("; path=" + this.path)) +
14991            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14992            ((this.secure == true) ? "; secure" : "");
14993     }
14994 });/*
14995  * Based on:
14996  * Ext JS Library 1.1.1
14997  * Copyright(c) 2006-2007, Ext JS, LLC.
14998  *
14999  * Originally Released Under LGPL - original licence link has changed is not relivant.
15000  *
15001  * Fork - LGPL
15002  * <script type="text/javascript">
15003  */
15004
15005
15006
15007 /*
15008  * These classes are derivatives of the similarly named classes in the YUI Library.
15009  * The original license:
15010  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
15011  * Code licensed under the BSD License:
15012  * http://developer.yahoo.net/yui/license.txt
15013  */
15014
15015 (function() {
15016
15017 var Event=Roo.EventManager;
15018 var Dom=Roo.lib.Dom;
15019
15020 /**
15021  * @class Roo.dd.DragDrop
15022  * @extends Roo.util.Observable
15023  * Defines the interface and base operation of items that that can be
15024  * dragged or can be drop targets.  It was designed to be extended, overriding
15025  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
15026  * Up to three html elements can be associated with a DragDrop instance:
15027  * <ul>
15028  * <li>linked element: the element that is passed into the constructor.
15029  * This is the element which defines the boundaries for interaction with
15030  * other DragDrop objects.</li>
15031  * <li>handle element(s): The drag operation only occurs if the element that
15032  * was clicked matches a handle element.  By default this is the linked
15033  * element, but there are times that you will want only a portion of the
15034  * linked element to initiate the drag operation, and the setHandleElId()
15035  * method provides a way to define this.</li>
15036  * <li>drag element: this represents the element that would be moved along
15037  * with the cursor during a drag operation.  By default, this is the linked
15038  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
15039  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
15040  * </li>
15041  * </ul>
15042  * This class should not be instantiated until the onload event to ensure that
15043  * the associated elements are available.
15044  * The following would define a DragDrop obj that would interact with any
15045  * other DragDrop obj in the "group1" group:
15046  * <pre>
15047  *  dd = new Roo.dd.DragDrop("div1", "group1");
15048  * </pre>
15049  * Since none of the event handlers have been implemented, nothing would
15050  * actually happen if you were to run the code above.  Normally you would
15051  * override this class or one of the default implementations, but you can
15052  * also override the methods you want on an instance of the class...
15053  * <pre>
15054  *  dd.onDragDrop = function(e, id) {
15055  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
15056  *  }
15057  * </pre>
15058  * @constructor
15059  * @param {String} id of the element that is linked to this instance
15060  * @param {String} sGroup the group of related DragDrop objects
15061  * @param {object} config an object containing configurable attributes
15062  *                Valid properties for DragDrop:
15063  *                    padding, isTarget, maintainOffset, primaryButtonOnly
15064  */
15065 Roo.dd.DragDrop = function(id, sGroup, config) {
15066     if (id) {
15067         this.init(id, sGroup, config);
15068     }
15069     
15070 };
15071
15072 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
15073
15074     /**
15075      * The id of the element associated with this object.  This is what we
15076      * refer to as the "linked element" because the size and position of
15077      * this element is used to determine when the drag and drop objects have
15078      * interacted.
15079      * @property id
15080      * @type String
15081      */
15082     id: null,
15083
15084     /**
15085      * Configuration attributes passed into the constructor
15086      * @property config
15087      * @type object
15088      */
15089     config: null,
15090
15091     /**
15092      * The id of the element that will be dragged.  By default this is same
15093      * as the linked element , but could be changed to another element. Ex:
15094      * Roo.dd.DDProxy
15095      * @property dragElId
15096      * @type String
15097      * @private
15098      */
15099     dragElId: null,
15100
15101     /**
15102      * the id of the element that initiates the drag operation.  By default
15103      * this is the linked element, but could be changed to be a child of this
15104      * element.  This lets us do things like only starting the drag when the
15105      * header element within the linked html element is clicked.
15106      * @property handleElId
15107      * @type String
15108      * @private
15109      */
15110     handleElId: null,
15111
15112     /**
15113      * An associative array of HTML tags that will be ignored if clicked.
15114      * @property invalidHandleTypes
15115      * @type {string: string}
15116      */
15117     invalidHandleTypes: null,
15118
15119     /**
15120      * An associative array of ids for elements that will be ignored if clicked
15121      * @property invalidHandleIds
15122      * @type {string: string}
15123      */
15124     invalidHandleIds: null,
15125
15126     /**
15127      * An indexted array of css class names for elements that will be ignored
15128      * if clicked.
15129      * @property invalidHandleClasses
15130      * @type string[]
15131      */
15132     invalidHandleClasses: null,
15133
15134     /**
15135      * The linked element's absolute X position at the time the drag was
15136      * started
15137      * @property startPageX
15138      * @type int
15139      * @private
15140      */
15141     startPageX: 0,
15142
15143     /**
15144      * The linked element's absolute X position at the time the drag was
15145      * started
15146      * @property startPageY
15147      * @type int
15148      * @private
15149      */
15150     startPageY: 0,
15151
15152     /**
15153      * The group defines a logical collection of DragDrop objects that are
15154      * related.  Instances only get events when interacting with other
15155      * DragDrop object in the same group.  This lets us define multiple
15156      * groups using a single DragDrop subclass if we want.
15157      * @property groups
15158      * @type {string: string}
15159      */
15160     groups: null,
15161
15162     /**
15163      * Individual drag/drop instances can be locked.  This will prevent
15164      * onmousedown start drag.
15165      * @property locked
15166      * @type boolean
15167      * @private
15168      */
15169     locked: false,
15170
15171     /**
15172      * Lock this instance
15173      * @method lock
15174      */
15175     lock: function() { this.locked = true; },
15176
15177     /**
15178      * Unlock this instace
15179      * @method unlock
15180      */
15181     unlock: function() { this.locked = false; },
15182
15183     /**
15184      * By default, all insances can be a drop target.  This can be disabled by
15185      * setting isTarget to false.
15186      * @method isTarget
15187      * @type boolean
15188      */
15189     isTarget: true,
15190
15191     /**
15192      * The padding configured for this drag and drop object for calculating
15193      * the drop zone intersection with this object.
15194      * @method padding
15195      * @type int[]
15196      */
15197     padding: null,
15198
15199     /**
15200      * Cached reference to the linked element
15201      * @property _domRef
15202      * @private
15203      */
15204     _domRef: null,
15205
15206     /**
15207      * Internal typeof flag
15208      * @property __ygDragDrop
15209      * @private
15210      */
15211     __ygDragDrop: true,
15212
15213     /**
15214      * Set to true when horizontal contraints are applied
15215      * @property constrainX
15216      * @type boolean
15217      * @private
15218      */
15219     constrainX: false,
15220
15221     /**
15222      * Set to true when vertical contraints are applied
15223      * @property constrainY
15224      * @type boolean
15225      * @private
15226      */
15227     constrainY: false,
15228
15229     /**
15230      * The left constraint
15231      * @property minX
15232      * @type int
15233      * @private
15234      */
15235     minX: 0,
15236
15237     /**
15238      * The right constraint
15239      * @property maxX
15240      * @type int
15241      * @private
15242      */
15243     maxX: 0,
15244
15245     /**
15246      * The up constraint
15247      * @property minY
15248      * @type int
15249      * @type int
15250      * @private
15251      */
15252     minY: 0,
15253
15254     /**
15255      * The down constraint
15256      * @property maxY
15257      * @type int
15258      * @private
15259      */
15260     maxY: 0,
15261
15262     /**
15263      * Maintain offsets when we resetconstraints.  Set to true when you want
15264      * the position of the element relative to its parent to stay the same
15265      * when the page changes
15266      *
15267      * @property maintainOffset
15268      * @type boolean
15269      */
15270     maintainOffset: false,
15271
15272     /**
15273      * Array of pixel locations the element will snap to if we specified a
15274      * horizontal graduation/interval.  This array is generated automatically
15275      * when you define a tick interval.
15276      * @property xTicks
15277      * @type int[]
15278      */
15279     xTicks: null,
15280
15281     /**
15282      * Array of pixel locations the element will snap to if we specified a
15283      * vertical graduation/interval.  This array is generated automatically
15284      * when you define a tick interval.
15285      * @property yTicks
15286      * @type int[]
15287      */
15288     yTicks: null,
15289
15290     /**
15291      * By default the drag and drop instance will only respond to the primary
15292      * button click (left button for a right-handed mouse).  Set to true to
15293      * allow drag and drop to start with any mouse click that is propogated
15294      * by the browser
15295      * @property primaryButtonOnly
15296      * @type boolean
15297      */
15298     primaryButtonOnly: true,
15299
15300     /**
15301      * The availabe property is false until the linked dom element is accessible.
15302      * @property available
15303      * @type boolean
15304      */
15305     available: false,
15306
15307     /**
15308      * By default, drags can only be initiated if the mousedown occurs in the
15309      * region the linked element is.  This is done in part to work around a
15310      * bug in some browsers that mis-report the mousedown if the previous
15311      * mouseup happened outside of the window.  This property is set to true
15312      * if outer handles are defined.
15313      *
15314      * @property hasOuterHandles
15315      * @type boolean
15316      * @default false
15317      */
15318     hasOuterHandles: false,
15319
15320     /**
15321      * Code that executes immediately before the startDrag event
15322      * @method b4StartDrag
15323      * @private
15324      */
15325     b4StartDrag: function(x, y) { },
15326
15327     /**
15328      * Abstract method called after a drag/drop object is clicked
15329      * and the drag or mousedown time thresholds have beeen met.
15330      * @method startDrag
15331      * @param {int} X click location
15332      * @param {int} Y click location
15333      */
15334     startDrag: function(x, y) { /* override this */ },
15335
15336     /**
15337      * Code that executes immediately before the onDrag event
15338      * @method b4Drag
15339      * @private
15340      */
15341     b4Drag: function(e) { },
15342
15343     /**
15344      * Abstract method called during the onMouseMove event while dragging an
15345      * object.
15346      * @method onDrag
15347      * @param {Event} e the mousemove event
15348      */
15349     onDrag: function(e) { /* override this */ },
15350
15351     /**
15352      * Abstract method called when this element fist begins hovering over
15353      * another DragDrop obj
15354      * @method onDragEnter
15355      * @param {Event} e the mousemove event
15356      * @param {String|DragDrop[]} id In POINT mode, the element
15357      * id this is hovering over.  In INTERSECT mode, an array of one or more
15358      * dragdrop items being hovered over.
15359      */
15360     onDragEnter: function(e, id) { /* override this */ },
15361
15362     /**
15363      * Code that executes immediately before the onDragOver event
15364      * @method b4DragOver
15365      * @private
15366      */
15367     b4DragOver: function(e) { },
15368
15369     /**
15370      * Abstract method called when this element is hovering over another
15371      * DragDrop obj
15372      * @method onDragOver
15373      * @param {Event} e the mousemove event
15374      * @param {String|DragDrop[]} id In POINT mode, the element
15375      * id this is hovering over.  In INTERSECT mode, an array of dd items
15376      * being hovered over.
15377      */
15378     onDragOver: function(e, id) { /* override this */ },
15379
15380     /**
15381      * Code that executes immediately before the onDragOut event
15382      * @method b4DragOut
15383      * @private
15384      */
15385     b4DragOut: function(e) { },
15386
15387     /**
15388      * Abstract method called when we are no longer hovering over an element
15389      * @method onDragOut
15390      * @param {Event} e the mousemove event
15391      * @param {String|DragDrop[]} id In POINT mode, the element
15392      * id this was hovering over.  In INTERSECT mode, an array of dd items
15393      * that the mouse is no longer over.
15394      */
15395     onDragOut: function(e, id) { /* override this */ },
15396
15397     /**
15398      * Code that executes immediately before the onDragDrop event
15399      * @method b4DragDrop
15400      * @private
15401      */
15402     b4DragDrop: function(e) { },
15403
15404     /**
15405      * Abstract method called when this item is dropped on another DragDrop
15406      * obj
15407      * @method onDragDrop
15408      * @param {Event} e the mouseup event
15409      * @param {String|DragDrop[]} id In POINT mode, the element
15410      * id this was dropped on.  In INTERSECT mode, an array of dd items this
15411      * was dropped on.
15412      */
15413     onDragDrop: function(e, id) { /* override this */ },
15414
15415     /**
15416      * Abstract method called when this item is dropped on an area with no
15417      * drop target
15418      * @method onInvalidDrop
15419      * @param {Event} e the mouseup event
15420      */
15421     onInvalidDrop: function(e) { /* override this */ },
15422
15423     /**
15424      * Code that executes immediately before the endDrag event
15425      * @method b4EndDrag
15426      * @private
15427      */
15428     b4EndDrag: function(e) { },
15429
15430     /**
15431      * Fired when we are done dragging the object
15432      * @method endDrag
15433      * @param {Event} e the mouseup event
15434      */
15435     endDrag: function(e) { /* override this */ },
15436
15437     /**
15438      * Code executed immediately before the onMouseDown event
15439      * @method b4MouseDown
15440      * @param {Event} e the mousedown event
15441      * @private
15442      */
15443     b4MouseDown: function(e) {  },
15444
15445     /**
15446      * Event handler that fires when a drag/drop obj gets a mousedown
15447      * @method onMouseDown
15448      * @param {Event} e the mousedown event
15449      */
15450     onMouseDown: function(e) { /* override this */ },
15451
15452     /**
15453      * Event handler that fires when a drag/drop obj gets a mouseup
15454      * @method onMouseUp
15455      * @param {Event} e the mouseup event
15456      */
15457     onMouseUp: function(e) { /* override this */ },
15458
15459     /**
15460      * Override the onAvailable method to do what is needed after the initial
15461      * position was determined.
15462      * @method onAvailable
15463      */
15464     onAvailable: function () {
15465     },
15466
15467     /*
15468      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
15469      * @type Object
15470      */
15471     defaultPadding : {left:0, right:0, top:0, bottom:0},
15472
15473     /*
15474      * Initializes the drag drop object's constraints to restrict movement to a certain element.
15475  *
15476  * Usage:
15477  <pre><code>
15478  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
15479                 { dragElId: "existingProxyDiv" });
15480  dd.startDrag = function(){
15481      this.constrainTo("parent-id");
15482  };
15483  </code></pre>
15484  * Or you can initalize it using the {@link Roo.Element} object:
15485  <pre><code>
15486  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
15487      startDrag : function(){
15488          this.constrainTo("parent-id");
15489      }
15490  });
15491  </code></pre>
15492      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
15493      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
15494      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
15495      * an object containing the sides to pad. For example: {right:10, bottom:10}
15496      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
15497      */
15498     constrainTo : function(constrainTo, pad, inContent){
15499         if(typeof pad == "number"){
15500             pad = {left: pad, right:pad, top:pad, bottom:pad};
15501         }
15502         pad = pad || this.defaultPadding;
15503         var b = Roo.get(this.getEl()).getBox();
15504         var ce = Roo.get(constrainTo);
15505         var s = ce.getScroll();
15506         var c, cd = ce.dom;
15507         if(cd == document.body){
15508             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15509         }else{
15510             xy = ce.getXY();
15511             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15512         }
15513
15514
15515         var topSpace = b.y - c.y;
15516         var leftSpace = b.x - c.x;
15517
15518         this.resetConstraints();
15519         this.setXConstraint(leftSpace - (pad.left||0), // left
15520                 c.width - leftSpace - b.width - (pad.right||0) //right
15521         );
15522         this.setYConstraint(topSpace - (pad.top||0), //top
15523                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15524         );
15525     },
15526
15527     /**
15528      * Returns a reference to the linked element
15529      * @method getEl
15530      * @return {HTMLElement} the html element
15531      */
15532     getEl: function() {
15533         if (!this._domRef) {
15534             this._domRef = Roo.getDom(this.id);
15535         }
15536
15537         return this._domRef;
15538     },
15539
15540     /**
15541      * Returns a reference to the actual element to drag.  By default this is
15542      * the same as the html element, but it can be assigned to another
15543      * element. An example of this can be found in Roo.dd.DDProxy
15544      * @method getDragEl
15545      * @return {HTMLElement} the html element
15546      */
15547     getDragEl: function() {
15548         return Roo.getDom(this.dragElId);
15549     },
15550
15551     /**
15552      * Sets up the DragDrop object.  Must be called in the constructor of any
15553      * Roo.dd.DragDrop subclass
15554      * @method init
15555      * @param id the id of the linked element
15556      * @param {String} sGroup the group of related items
15557      * @param {object} config configuration attributes
15558      */
15559     init: function(id, sGroup, config) {
15560         this.initTarget(id, sGroup, config);
15561         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15562         // Event.on(this.id, "selectstart", Event.preventDefault);
15563     },
15564
15565     /**
15566      * Initializes Targeting functionality only... the object does not
15567      * get a mousedown handler.
15568      * @method initTarget
15569      * @param id the id of the linked element
15570      * @param {String} sGroup the group of related items
15571      * @param {object} config configuration attributes
15572      */
15573     initTarget: function(id, sGroup, config) {
15574
15575         // configuration attributes
15576         this.config = config || {};
15577
15578         // create a local reference to the drag and drop manager
15579         this.DDM = Roo.dd.DDM;
15580         // initialize the groups array
15581         this.groups = {};
15582
15583         // assume that we have an element reference instead of an id if the
15584         // parameter is not a string
15585         if (typeof id !== "string") {
15586             id = Roo.id(id);
15587         }
15588
15589         // set the id
15590         this.id = id;
15591
15592         // add to an interaction group
15593         this.addToGroup((sGroup) ? sGroup : "default");
15594
15595         // We don't want to register this as the handle with the manager
15596         // so we just set the id rather than calling the setter.
15597         this.handleElId = id;
15598
15599         // the linked element is the element that gets dragged by default
15600         this.setDragElId(id);
15601
15602         // by default, clicked anchors will not start drag operations.
15603         this.invalidHandleTypes = { A: "A" };
15604         this.invalidHandleIds = {};
15605         this.invalidHandleClasses = [];
15606
15607         this.applyConfig();
15608
15609         this.handleOnAvailable();
15610     },
15611
15612     /**
15613      * Applies the configuration parameters that were passed into the constructor.
15614      * This is supposed to happen at each level through the inheritance chain.  So
15615      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15616      * DragDrop in order to get all of the parameters that are available in
15617      * each object.
15618      * @method applyConfig
15619      */
15620     applyConfig: function() {
15621
15622         // configurable properties:
15623         //    padding, isTarget, maintainOffset, primaryButtonOnly
15624         this.padding           = this.config.padding || [0, 0, 0, 0];
15625         this.isTarget          = (this.config.isTarget !== false);
15626         this.maintainOffset    = (this.config.maintainOffset);
15627         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15628
15629     },
15630
15631     /**
15632      * Executed when the linked element is available
15633      * @method handleOnAvailable
15634      * @private
15635      */
15636     handleOnAvailable: function() {
15637         this.available = true;
15638         this.resetConstraints();
15639         this.onAvailable();
15640     },
15641
15642      /**
15643      * Configures the padding for the target zone in px.  Effectively expands
15644      * (or reduces) the virtual object size for targeting calculations.
15645      * Supports css-style shorthand; if only one parameter is passed, all sides
15646      * will have that padding, and if only two are passed, the top and bottom
15647      * will have the first param, the left and right the second.
15648      * @method setPadding
15649      * @param {int} iTop    Top pad
15650      * @param {int} iRight  Right pad
15651      * @param {int} iBot    Bot pad
15652      * @param {int} iLeft   Left pad
15653      */
15654     setPadding: function(iTop, iRight, iBot, iLeft) {
15655         // this.padding = [iLeft, iRight, iTop, iBot];
15656         if (!iRight && 0 !== iRight) {
15657             this.padding = [iTop, iTop, iTop, iTop];
15658         } else if (!iBot && 0 !== iBot) {
15659             this.padding = [iTop, iRight, iTop, iRight];
15660         } else {
15661             this.padding = [iTop, iRight, iBot, iLeft];
15662         }
15663     },
15664
15665     /**
15666      * Stores the initial placement of the linked element.
15667      * @method setInitialPosition
15668      * @param {int} diffX   the X offset, default 0
15669      * @param {int} diffY   the Y offset, default 0
15670      */
15671     setInitPosition: function(diffX, diffY) {
15672         var el = this.getEl();
15673
15674         if (!this.DDM.verifyEl(el)) {
15675             return;
15676         }
15677
15678         var dx = diffX || 0;
15679         var dy = diffY || 0;
15680
15681         var p = Dom.getXY( el );
15682
15683         this.initPageX = p[0] - dx;
15684         this.initPageY = p[1] - dy;
15685
15686         this.lastPageX = p[0];
15687         this.lastPageY = p[1];
15688
15689
15690         this.setStartPosition(p);
15691     },
15692
15693     /**
15694      * Sets the start position of the element.  This is set when the obj
15695      * is initialized, the reset when a drag is started.
15696      * @method setStartPosition
15697      * @param pos current position (from previous lookup)
15698      * @private
15699      */
15700     setStartPosition: function(pos) {
15701         var p = pos || Dom.getXY( this.getEl() );
15702         this.deltaSetXY = null;
15703
15704         this.startPageX = p[0];
15705         this.startPageY = p[1];
15706     },
15707
15708     /**
15709      * Add this instance to a group of related drag/drop objects.  All
15710      * instances belong to at least one group, and can belong to as many
15711      * groups as needed.
15712      * @method addToGroup
15713      * @param sGroup {string} the name of the group
15714      */
15715     addToGroup: function(sGroup) {
15716         this.groups[sGroup] = true;
15717         this.DDM.regDragDrop(this, sGroup);
15718     },
15719
15720     /**
15721      * Remove's this instance from the supplied interaction group
15722      * @method removeFromGroup
15723      * @param {string}  sGroup  The group to drop
15724      */
15725     removeFromGroup: function(sGroup) {
15726         if (this.groups[sGroup]) {
15727             delete this.groups[sGroup];
15728         }
15729
15730         this.DDM.removeDDFromGroup(this, sGroup);
15731     },
15732
15733     /**
15734      * Allows you to specify that an element other than the linked element
15735      * will be moved with the cursor during a drag
15736      * @method setDragElId
15737      * @param id {string} the id of the element that will be used to initiate the drag
15738      */
15739     setDragElId: function(id) {
15740         this.dragElId = id;
15741     },
15742
15743     /**
15744      * Allows you to specify a child of the linked element that should be
15745      * used to initiate the drag operation.  An example of this would be if
15746      * you have a content div with text and links.  Clicking anywhere in the
15747      * content area would normally start the drag operation.  Use this method
15748      * to specify that an element inside of the content div is the element
15749      * that starts the drag operation.
15750      * @method setHandleElId
15751      * @param id {string} the id of the element that will be used to
15752      * initiate the drag.
15753      */
15754     setHandleElId: function(id) {
15755         if (typeof id !== "string") {
15756             id = Roo.id(id);
15757         }
15758         this.handleElId = id;
15759         this.DDM.regHandle(this.id, id);
15760     },
15761
15762     /**
15763      * Allows you to set an element outside of the linked element as a drag
15764      * handle
15765      * @method setOuterHandleElId
15766      * @param id the id of the element that will be used to initiate the drag
15767      */
15768     setOuterHandleElId: function(id) {
15769         if (typeof id !== "string") {
15770             id = Roo.id(id);
15771         }
15772         Event.on(id, "mousedown",
15773                 this.handleMouseDown, this);
15774         this.setHandleElId(id);
15775
15776         this.hasOuterHandles = true;
15777     },
15778
15779     /**
15780      * Remove all drag and drop hooks for this element
15781      * @method unreg
15782      */
15783     unreg: function() {
15784         Event.un(this.id, "mousedown",
15785                 this.handleMouseDown);
15786         this._domRef = null;
15787         this.DDM._remove(this);
15788     },
15789
15790     destroy : function(){
15791         this.unreg();
15792     },
15793
15794     /**
15795      * Returns true if this instance is locked, or the drag drop mgr is locked
15796      * (meaning that all drag/drop is disabled on the page.)
15797      * @method isLocked
15798      * @return {boolean} true if this obj or all drag/drop is locked, else
15799      * false
15800      */
15801     isLocked: function() {
15802         return (this.DDM.isLocked() || this.locked);
15803     },
15804
15805     /**
15806      * Fired when this object is clicked
15807      * @method handleMouseDown
15808      * @param {Event} e
15809      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15810      * @private
15811      */
15812     handleMouseDown: function(e, oDD){
15813         if (this.primaryButtonOnly && e.button != 0) {
15814             return;
15815         }
15816
15817         if (this.isLocked()) {
15818             return;
15819         }
15820
15821         this.DDM.refreshCache(this.groups);
15822
15823         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15824         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15825         } else {
15826             if (this.clickValidator(e)) {
15827
15828                 // set the initial element position
15829                 this.setStartPosition();
15830
15831
15832                 this.b4MouseDown(e);
15833                 this.onMouseDown(e);
15834
15835                 this.DDM.handleMouseDown(e, this);
15836
15837                 this.DDM.stopEvent(e);
15838             } else {
15839
15840
15841             }
15842         }
15843     },
15844
15845     clickValidator: function(e) {
15846         var target = e.getTarget();
15847         return ( this.isValidHandleChild(target) &&
15848                     (this.id == this.handleElId ||
15849                         this.DDM.handleWasClicked(target, this.id)) );
15850     },
15851
15852     /**
15853      * Allows you to specify a tag name that should not start a drag operation
15854      * when clicked.  This is designed to facilitate embedding links within a
15855      * drag handle that do something other than start the drag.
15856      * @method addInvalidHandleType
15857      * @param {string} tagName the type of element to exclude
15858      */
15859     addInvalidHandleType: function(tagName) {
15860         var type = tagName.toUpperCase();
15861         this.invalidHandleTypes[type] = type;
15862     },
15863
15864     /**
15865      * Lets you to specify an element id for a child of a drag handle
15866      * that should not initiate a drag
15867      * @method addInvalidHandleId
15868      * @param {string} id the element id of the element you wish to ignore
15869      */
15870     addInvalidHandleId: function(id) {
15871         if (typeof id !== "string") {
15872             id = Roo.id(id);
15873         }
15874         this.invalidHandleIds[id] = id;
15875     },
15876
15877     /**
15878      * Lets you specify a css class of elements that will not initiate a drag
15879      * @method addInvalidHandleClass
15880      * @param {string} cssClass the class of the elements you wish to ignore
15881      */
15882     addInvalidHandleClass: function(cssClass) {
15883         this.invalidHandleClasses.push(cssClass);
15884     },
15885
15886     /**
15887      * Unsets an excluded tag name set by addInvalidHandleType
15888      * @method removeInvalidHandleType
15889      * @param {string} tagName the type of element to unexclude
15890      */
15891     removeInvalidHandleType: function(tagName) {
15892         var type = tagName.toUpperCase();
15893         // this.invalidHandleTypes[type] = null;
15894         delete this.invalidHandleTypes[type];
15895     },
15896
15897     /**
15898      * Unsets an invalid handle id
15899      * @method removeInvalidHandleId
15900      * @param {string} id the id of the element to re-enable
15901      */
15902     removeInvalidHandleId: function(id) {
15903         if (typeof id !== "string") {
15904             id = Roo.id(id);
15905         }
15906         delete this.invalidHandleIds[id];
15907     },
15908
15909     /**
15910      * Unsets an invalid css class
15911      * @method removeInvalidHandleClass
15912      * @param {string} cssClass the class of the element(s) you wish to
15913      * re-enable
15914      */
15915     removeInvalidHandleClass: function(cssClass) {
15916         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15917             if (this.invalidHandleClasses[i] == cssClass) {
15918                 delete this.invalidHandleClasses[i];
15919             }
15920         }
15921     },
15922
15923     /**
15924      * Checks the tag exclusion list to see if this click should be ignored
15925      * @method isValidHandleChild
15926      * @param {HTMLElement} node the HTMLElement to evaluate
15927      * @return {boolean} true if this is a valid tag type, false if not
15928      */
15929     isValidHandleChild: function(node) {
15930
15931         var valid = true;
15932         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15933         var nodeName;
15934         try {
15935             nodeName = node.nodeName.toUpperCase();
15936         } catch(e) {
15937             nodeName = node.nodeName;
15938         }
15939         valid = valid && !this.invalidHandleTypes[nodeName];
15940         valid = valid && !this.invalidHandleIds[node.id];
15941
15942         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15943             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15944         }
15945
15946
15947         return valid;
15948
15949     },
15950
15951     /**
15952      * Create the array of horizontal tick marks if an interval was specified
15953      * in setXConstraint().
15954      * @method setXTicks
15955      * @private
15956      */
15957     setXTicks: function(iStartX, iTickSize) {
15958         this.xTicks = [];
15959         this.xTickSize = iTickSize;
15960
15961         var tickMap = {};
15962
15963         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15964             if (!tickMap[i]) {
15965                 this.xTicks[this.xTicks.length] = i;
15966                 tickMap[i] = true;
15967             }
15968         }
15969
15970         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15971             if (!tickMap[i]) {
15972                 this.xTicks[this.xTicks.length] = i;
15973                 tickMap[i] = true;
15974             }
15975         }
15976
15977         this.xTicks.sort(this.DDM.numericSort) ;
15978     },
15979
15980     /**
15981      * Create the array of vertical tick marks if an interval was specified in
15982      * setYConstraint().
15983      * @method setYTicks
15984      * @private
15985      */
15986     setYTicks: function(iStartY, iTickSize) {
15987         this.yTicks = [];
15988         this.yTickSize = iTickSize;
15989
15990         var tickMap = {};
15991
15992         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15993             if (!tickMap[i]) {
15994                 this.yTicks[this.yTicks.length] = i;
15995                 tickMap[i] = true;
15996             }
15997         }
15998
15999         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
16000             if (!tickMap[i]) {
16001                 this.yTicks[this.yTicks.length] = i;
16002                 tickMap[i] = true;
16003             }
16004         }
16005
16006         this.yTicks.sort(this.DDM.numericSort) ;
16007     },
16008
16009     /**
16010      * By default, the element can be dragged any place on the screen.  Use
16011      * this method to limit the horizontal travel of the element.  Pass in
16012      * 0,0 for the parameters if you want to lock the drag to the y axis.
16013      * @method setXConstraint
16014      * @param {int} iLeft the number of pixels the element can move to the left
16015      * @param {int} iRight the number of pixels the element can move to the
16016      * right
16017      * @param {int} iTickSize optional parameter for specifying that the
16018      * element
16019      * should move iTickSize pixels at a time.
16020      */
16021     setXConstraint: function(iLeft, iRight, iTickSize) {
16022         this.leftConstraint = iLeft;
16023         this.rightConstraint = iRight;
16024
16025         this.minX = this.initPageX - iLeft;
16026         this.maxX = this.initPageX + iRight;
16027         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
16028
16029         this.constrainX = true;
16030     },
16031
16032     /**
16033      * Clears any constraints applied to this instance.  Also clears ticks
16034      * since they can't exist independent of a constraint at this time.
16035      * @method clearConstraints
16036      */
16037     clearConstraints: function() {
16038         this.constrainX = false;
16039         this.constrainY = false;
16040         this.clearTicks();
16041     },
16042
16043     /**
16044      * Clears any tick interval defined for this instance
16045      * @method clearTicks
16046      */
16047     clearTicks: function() {
16048         this.xTicks = null;
16049         this.yTicks = null;
16050         this.xTickSize = 0;
16051         this.yTickSize = 0;
16052     },
16053
16054     /**
16055      * By default, the element can be dragged any place on the screen.  Set
16056      * this to limit the vertical travel of the element.  Pass in 0,0 for the
16057      * parameters if you want to lock the drag to the x axis.
16058      * @method setYConstraint
16059      * @param {int} iUp the number of pixels the element can move up
16060      * @param {int} iDown the number of pixels the element can move down
16061      * @param {int} iTickSize optional parameter for specifying that the
16062      * element should move iTickSize pixels at a time.
16063      */
16064     setYConstraint: function(iUp, iDown, iTickSize) {
16065         this.topConstraint = iUp;
16066         this.bottomConstraint = iDown;
16067
16068         this.minY = this.initPageY - iUp;
16069         this.maxY = this.initPageY + iDown;
16070         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
16071
16072         this.constrainY = true;
16073
16074     },
16075
16076     /**
16077      * resetConstraints must be called if you manually reposition a dd element.
16078      * @method resetConstraints
16079      * @param {boolean} maintainOffset
16080      */
16081     resetConstraints: function() {
16082
16083
16084         // Maintain offsets if necessary
16085         if (this.initPageX || this.initPageX === 0) {
16086             // figure out how much this thing has moved
16087             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
16088             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
16089
16090             this.setInitPosition(dx, dy);
16091
16092         // This is the first time we have detected the element's position
16093         } else {
16094             this.setInitPosition();
16095         }
16096
16097         if (this.constrainX) {
16098             this.setXConstraint( this.leftConstraint,
16099                                  this.rightConstraint,
16100                                  this.xTickSize        );
16101         }
16102
16103         if (this.constrainY) {
16104             this.setYConstraint( this.topConstraint,
16105                                  this.bottomConstraint,
16106                                  this.yTickSize         );
16107         }
16108     },
16109
16110     /**
16111      * Normally the drag element is moved pixel by pixel, but we can specify
16112      * that it move a number of pixels at a time.  This method resolves the
16113      * location when we have it set up like this.
16114      * @method getTick
16115      * @param {int} val where we want to place the object
16116      * @param {int[]} tickArray sorted array of valid points
16117      * @return {int} the closest tick
16118      * @private
16119      */
16120     getTick: function(val, tickArray) {
16121
16122         if (!tickArray) {
16123             // If tick interval is not defined, it is effectively 1 pixel,
16124             // so we return the value passed to us.
16125             return val;
16126         } else if (tickArray[0] >= val) {
16127             // The value is lower than the first tick, so we return the first
16128             // tick.
16129             return tickArray[0];
16130         } else {
16131             for (var i=0, len=tickArray.length; i<len; ++i) {
16132                 var next = i + 1;
16133                 if (tickArray[next] && tickArray[next] >= val) {
16134                     var diff1 = val - tickArray[i];
16135                     var diff2 = tickArray[next] - val;
16136                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
16137                 }
16138             }
16139
16140             // The value is larger than the last tick, so we return the last
16141             // tick.
16142             return tickArray[tickArray.length - 1];
16143         }
16144     },
16145
16146     /**
16147      * toString method
16148      * @method toString
16149      * @return {string} string representation of the dd obj
16150      */
16151     toString: function() {
16152         return ("DragDrop " + this.id);
16153     }
16154
16155 });
16156
16157 })();
16158 /*
16159  * Based on:
16160  * Ext JS Library 1.1.1
16161  * Copyright(c) 2006-2007, Ext JS, LLC.
16162  *
16163  * Originally Released Under LGPL - original licence link has changed is not relivant.
16164  *
16165  * Fork - LGPL
16166  * <script type="text/javascript">
16167  */
16168
16169
16170 /**
16171  * The drag and drop utility provides a framework for building drag and drop
16172  * applications.  In addition to enabling drag and drop for specific elements,
16173  * the drag and drop elements are tracked by the manager class, and the
16174  * interactions between the various elements are tracked during the drag and
16175  * the implementing code is notified about these important moments.
16176  */
16177
16178 // Only load the library once.  Rewriting the manager class would orphan
16179 // existing drag and drop instances.
16180 if (!Roo.dd.DragDropMgr) {
16181
16182 /**
16183  * @class Roo.dd.DragDropMgr
16184  * DragDropMgr is a singleton that tracks the element interaction for
16185  * all DragDrop items in the window.  Generally, you will not call
16186  * this class directly, but it does have helper methods that could
16187  * be useful in your DragDrop implementations.
16188  * @singleton
16189  */
16190 Roo.dd.DragDropMgr = function() {
16191
16192     var Event = Roo.EventManager;
16193
16194     return {
16195
16196         /**
16197          * Two dimensional Array of registered DragDrop objects.  The first
16198          * dimension is the DragDrop item group, the second the DragDrop
16199          * object.
16200          * @property ids
16201          * @type {string: string}
16202          * @private
16203          * @static
16204          */
16205         ids: {},
16206
16207         /**
16208          * Array of element ids defined as drag handles.  Used to determine
16209          * if the element that generated the mousedown event is actually the
16210          * handle and not the html element itself.
16211          * @property handleIds
16212          * @type {string: string}
16213          * @private
16214          * @static
16215          */
16216         handleIds: {},
16217
16218         /**
16219          * the DragDrop object that is currently being dragged
16220          * @property dragCurrent
16221          * @type DragDrop
16222          * @private
16223          * @static
16224          **/
16225         dragCurrent: null,
16226
16227         /**
16228          * the DragDrop object(s) that are being hovered over
16229          * @property dragOvers
16230          * @type Array
16231          * @private
16232          * @static
16233          */
16234         dragOvers: {},
16235
16236         /**
16237          * the X distance between the cursor and the object being dragged
16238          * @property deltaX
16239          * @type int
16240          * @private
16241          * @static
16242          */
16243         deltaX: 0,
16244
16245         /**
16246          * the Y distance between the cursor and the object being dragged
16247          * @property deltaY
16248          * @type int
16249          * @private
16250          * @static
16251          */
16252         deltaY: 0,
16253
16254         /**
16255          * Flag to determine if we should prevent the default behavior of the
16256          * events we define. By default this is true, but this can be set to
16257          * false if you need the default behavior (not recommended)
16258          * @property preventDefault
16259          * @type boolean
16260          * @static
16261          */
16262         preventDefault: true,
16263
16264         /**
16265          * Flag to determine if we should stop the propagation of the events
16266          * we generate. This is true by default but you may want to set it to
16267          * false if the html element contains other features that require the
16268          * mouse click.
16269          * @property stopPropagation
16270          * @type boolean
16271          * @static
16272          */
16273         stopPropagation: true,
16274
16275         /**
16276          * Internal flag that is set to true when drag and drop has been
16277          * intialized
16278          * @property initialized
16279          * @private
16280          * @static
16281          */
16282         initalized: false,
16283
16284         /**
16285          * All drag and drop can be disabled.
16286          * @property locked
16287          * @private
16288          * @static
16289          */
16290         locked: false,
16291
16292         /**
16293          * Called the first time an element is registered.
16294          * @method init
16295          * @private
16296          * @static
16297          */
16298         init: function() {
16299             this.initialized = true;
16300         },
16301
16302         /**
16303          * In point mode, drag and drop interaction is defined by the
16304          * location of the cursor during the drag/drop
16305          * @property POINT
16306          * @type int
16307          * @static
16308          */
16309         POINT: 0,
16310
16311         /**
16312          * In intersect mode, drag and drop interactio nis defined by the
16313          * overlap of two or more drag and drop objects.
16314          * @property INTERSECT
16315          * @type int
16316          * @static
16317          */
16318         INTERSECT: 1,
16319
16320         /**
16321          * The current drag and drop mode.  Default: POINT
16322          * @property mode
16323          * @type int
16324          * @static
16325          */
16326         mode: 0,
16327
16328         /**
16329          * Runs method on all drag and drop objects
16330          * @method _execOnAll
16331          * @private
16332          * @static
16333          */
16334         _execOnAll: function(sMethod, args) {
16335             for (var i in this.ids) {
16336                 for (var j in this.ids[i]) {
16337                     var oDD = this.ids[i][j];
16338                     if (! this.isTypeOfDD(oDD)) {
16339                         continue;
16340                     }
16341                     oDD[sMethod].apply(oDD, args);
16342                 }
16343             }
16344         },
16345
16346         /**
16347          * Drag and drop initialization.  Sets up the global event handlers
16348          * @method _onLoad
16349          * @private
16350          * @static
16351          */
16352         _onLoad: function() {
16353
16354             this.init();
16355
16356
16357             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
16358             Event.on(document, "mousemove", this.handleMouseMove, this, true);
16359             Event.on(window,   "unload",    this._onUnload, this, true);
16360             Event.on(window,   "resize",    this._onResize, this, true);
16361             // Event.on(window,   "mouseout",    this._test);
16362
16363         },
16364
16365         /**
16366          * Reset constraints on all drag and drop objs
16367          * @method _onResize
16368          * @private
16369          * @static
16370          */
16371         _onResize: function(e) {
16372             this._execOnAll("resetConstraints", []);
16373         },
16374
16375         /**
16376          * Lock all drag and drop functionality
16377          * @method lock
16378          * @static
16379          */
16380         lock: function() { this.locked = true; },
16381
16382         /**
16383          * Unlock all drag and drop functionality
16384          * @method unlock
16385          * @static
16386          */
16387         unlock: function() { this.locked = false; },
16388
16389         /**
16390          * Is drag and drop locked?
16391          * @method isLocked
16392          * @return {boolean} True if drag and drop is locked, false otherwise.
16393          * @static
16394          */
16395         isLocked: function() { return this.locked; },
16396
16397         /**
16398          * Location cache that is set for all drag drop objects when a drag is
16399          * initiated, cleared when the drag is finished.
16400          * @property locationCache
16401          * @private
16402          * @static
16403          */
16404         locationCache: {},
16405
16406         /**
16407          * Set useCache to false if you want to force object the lookup of each
16408          * drag and drop linked element constantly during a drag.
16409          * @property useCache
16410          * @type boolean
16411          * @static
16412          */
16413         useCache: true,
16414
16415         /**
16416          * The number of pixels that the mouse needs to move after the
16417          * mousedown before the drag is initiated.  Default=3;
16418          * @property clickPixelThresh
16419          * @type int
16420          * @static
16421          */
16422         clickPixelThresh: 3,
16423
16424         /**
16425          * The number of milliseconds after the mousedown event to initiate the
16426          * drag if we don't get a mouseup event. Default=1000
16427          * @property clickTimeThresh
16428          * @type int
16429          * @static
16430          */
16431         clickTimeThresh: 350,
16432
16433         /**
16434          * Flag that indicates that either the drag pixel threshold or the
16435          * mousdown time threshold has been met
16436          * @property dragThreshMet
16437          * @type boolean
16438          * @private
16439          * @static
16440          */
16441         dragThreshMet: false,
16442
16443         /**
16444          * Timeout used for the click time threshold
16445          * @property clickTimeout
16446          * @type Object
16447          * @private
16448          * @static
16449          */
16450         clickTimeout: null,
16451
16452         /**
16453          * The X position of the mousedown event stored for later use when a
16454          * drag threshold is met.
16455          * @property startX
16456          * @type int
16457          * @private
16458          * @static
16459          */
16460         startX: 0,
16461
16462         /**
16463          * The Y position of the mousedown event stored for later use when a
16464          * drag threshold is met.
16465          * @property startY
16466          * @type int
16467          * @private
16468          * @static
16469          */
16470         startY: 0,
16471
16472         /**
16473          * Each DragDrop instance must be registered with the DragDropMgr.
16474          * This is executed in DragDrop.init()
16475          * @method regDragDrop
16476          * @param {DragDrop} oDD the DragDrop object to register
16477          * @param {String} sGroup the name of the group this element belongs to
16478          * @static
16479          */
16480         regDragDrop: function(oDD, sGroup) {
16481             if (!this.initialized) { this.init(); }
16482
16483             if (!this.ids[sGroup]) {
16484                 this.ids[sGroup] = {};
16485             }
16486             this.ids[sGroup][oDD.id] = oDD;
16487         },
16488
16489         /**
16490          * Removes the supplied dd instance from the supplied group. Executed
16491          * by DragDrop.removeFromGroup, so don't call this function directly.
16492          * @method removeDDFromGroup
16493          * @private
16494          * @static
16495          */
16496         removeDDFromGroup: function(oDD, sGroup) {
16497             if (!this.ids[sGroup]) {
16498                 this.ids[sGroup] = {};
16499             }
16500
16501             var obj = this.ids[sGroup];
16502             if (obj && obj[oDD.id]) {
16503                 delete obj[oDD.id];
16504             }
16505         },
16506
16507         /**
16508          * Unregisters a drag and drop item.  This is executed in
16509          * DragDrop.unreg, use that method instead of calling this directly.
16510          * @method _remove
16511          * @private
16512          * @static
16513          */
16514         _remove: function(oDD) {
16515             for (var g in oDD.groups) {
16516                 if (g && this.ids[g][oDD.id]) {
16517                     delete this.ids[g][oDD.id];
16518                 }
16519             }
16520             delete this.handleIds[oDD.id];
16521         },
16522
16523         /**
16524          * Each DragDrop handle element must be registered.  This is done
16525          * automatically when executing DragDrop.setHandleElId()
16526          * @method regHandle
16527          * @param {String} sDDId the DragDrop id this element is a handle for
16528          * @param {String} sHandleId the id of the element that is the drag
16529          * handle
16530          * @static
16531          */
16532         regHandle: function(sDDId, sHandleId) {
16533             if (!this.handleIds[sDDId]) {
16534                 this.handleIds[sDDId] = {};
16535             }
16536             this.handleIds[sDDId][sHandleId] = sHandleId;
16537         },
16538
16539         /**
16540          * Utility function to determine if a given element has been
16541          * registered as a drag drop item.
16542          * @method isDragDrop
16543          * @param {String} id the element id to check
16544          * @return {boolean} true if this element is a DragDrop item,
16545          * false otherwise
16546          * @static
16547          */
16548         isDragDrop: function(id) {
16549             return ( this.getDDById(id) ) ? true : false;
16550         },
16551
16552         /**
16553          * Returns the drag and drop instances that are in all groups the
16554          * passed in instance belongs to.
16555          * @method getRelated
16556          * @param {DragDrop} p_oDD the obj to get related data for
16557          * @param {boolean} bTargetsOnly if true, only return targetable objs
16558          * @return {DragDrop[]} the related instances
16559          * @static
16560          */
16561         getRelated: function(p_oDD, bTargetsOnly) {
16562             var oDDs = [];
16563             for (var i in p_oDD.groups) {
16564                 for (j in this.ids[i]) {
16565                     var dd = this.ids[i][j];
16566                     if (! this.isTypeOfDD(dd)) {
16567                         continue;
16568                     }
16569                     if (!bTargetsOnly || dd.isTarget) {
16570                         oDDs[oDDs.length] = dd;
16571                     }
16572                 }
16573             }
16574
16575             return oDDs;
16576         },
16577
16578         /**
16579          * Returns true if the specified dd target is a legal target for
16580          * the specifice drag obj
16581          * @method isLegalTarget
16582          * @param {DragDrop} the drag obj
16583          * @param {DragDrop} the target
16584          * @return {boolean} true if the target is a legal target for the
16585          * dd obj
16586          * @static
16587          */
16588         isLegalTarget: function (oDD, oTargetDD) {
16589             var targets = this.getRelated(oDD, true);
16590             for (var i=0, len=targets.length;i<len;++i) {
16591                 if (targets[i].id == oTargetDD.id) {
16592                     return true;
16593                 }
16594             }
16595
16596             return false;
16597         },
16598
16599         /**
16600          * My goal is to be able to transparently determine if an object is
16601          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16602          * returns "object", oDD.constructor.toString() always returns
16603          * "DragDrop" and not the name of the subclass.  So for now it just
16604          * evaluates a well-known variable in DragDrop.
16605          * @method isTypeOfDD
16606          * @param {Object} the object to evaluate
16607          * @return {boolean} true if typeof oDD = DragDrop
16608          * @static
16609          */
16610         isTypeOfDD: function (oDD) {
16611             return (oDD && oDD.__ygDragDrop);
16612         },
16613
16614         /**
16615          * Utility function to determine if a given element has been
16616          * registered as a drag drop handle for the given Drag Drop object.
16617          * @method isHandle
16618          * @param {String} id the element id to check
16619          * @return {boolean} true if this element is a DragDrop handle, false
16620          * otherwise
16621          * @static
16622          */
16623         isHandle: function(sDDId, sHandleId) {
16624             return ( this.handleIds[sDDId] &&
16625                             this.handleIds[sDDId][sHandleId] );
16626         },
16627
16628         /**
16629          * Returns the DragDrop instance for a given id
16630          * @method getDDById
16631          * @param {String} id the id of the DragDrop object
16632          * @return {DragDrop} the drag drop object, null if it is not found
16633          * @static
16634          */
16635         getDDById: function(id) {
16636             for (var i in this.ids) {
16637                 if (this.ids[i][id]) {
16638                     return this.ids[i][id];
16639                 }
16640             }
16641             return null;
16642         },
16643
16644         /**
16645          * Fired after a registered DragDrop object gets the mousedown event.
16646          * Sets up the events required to track the object being dragged
16647          * @method handleMouseDown
16648          * @param {Event} e the event
16649          * @param oDD the DragDrop object being dragged
16650          * @private
16651          * @static
16652          */
16653         handleMouseDown: function(e, oDD) {
16654             if(Roo.QuickTips){
16655                 Roo.QuickTips.disable();
16656             }
16657             this.currentTarget = e.getTarget();
16658
16659             this.dragCurrent = oDD;
16660
16661             var el = oDD.getEl();
16662
16663             // track start position
16664             this.startX = e.getPageX();
16665             this.startY = e.getPageY();
16666
16667             this.deltaX = this.startX - el.offsetLeft;
16668             this.deltaY = this.startY - el.offsetTop;
16669
16670             this.dragThreshMet = false;
16671
16672             this.clickTimeout = setTimeout(
16673                     function() {
16674                         var DDM = Roo.dd.DDM;
16675                         DDM.startDrag(DDM.startX, DDM.startY);
16676                     },
16677                     this.clickTimeThresh );
16678         },
16679
16680         /**
16681          * Fired when either the drag pixel threshol or the mousedown hold
16682          * time threshold has been met.
16683          * @method startDrag
16684          * @param x {int} the X position of the original mousedown
16685          * @param y {int} the Y position of the original mousedown
16686          * @static
16687          */
16688         startDrag: function(x, y) {
16689             clearTimeout(this.clickTimeout);
16690             if (this.dragCurrent) {
16691                 this.dragCurrent.b4StartDrag(x, y);
16692                 this.dragCurrent.startDrag(x, y);
16693             }
16694             this.dragThreshMet = true;
16695         },
16696
16697         /**
16698          * Internal function to handle the mouseup event.  Will be invoked
16699          * from the context of the document.
16700          * @method handleMouseUp
16701          * @param {Event} e the event
16702          * @private
16703          * @static
16704          */
16705         handleMouseUp: function(e) {
16706
16707             if(Roo.QuickTips){
16708                 Roo.QuickTips.enable();
16709             }
16710             if (! this.dragCurrent) {
16711                 return;
16712             }
16713
16714             clearTimeout(this.clickTimeout);
16715
16716             if (this.dragThreshMet) {
16717                 this.fireEvents(e, true);
16718             } else {
16719             }
16720
16721             this.stopDrag(e);
16722
16723             this.stopEvent(e);
16724         },
16725
16726         /**
16727          * Utility to stop event propagation and event default, if these
16728          * features are turned on.
16729          * @method stopEvent
16730          * @param {Event} e the event as returned by this.getEvent()
16731          * @static
16732          */
16733         stopEvent: function(e){
16734             if(this.stopPropagation) {
16735                 e.stopPropagation();
16736             }
16737
16738             if (this.preventDefault) {
16739                 e.preventDefault();
16740             }
16741         },
16742
16743         /**
16744          * Internal function to clean up event handlers after the drag
16745          * operation is complete
16746          * @method stopDrag
16747          * @param {Event} e the event
16748          * @private
16749          * @static
16750          */
16751         stopDrag: function(e) {
16752             // Fire the drag end event for the item that was dragged
16753             if (this.dragCurrent) {
16754                 if (this.dragThreshMet) {
16755                     this.dragCurrent.b4EndDrag(e);
16756                     this.dragCurrent.endDrag(e);
16757                 }
16758
16759                 this.dragCurrent.onMouseUp(e);
16760             }
16761
16762             this.dragCurrent = null;
16763             this.dragOvers = {};
16764         },
16765
16766         /**
16767          * Internal function to handle the mousemove event.  Will be invoked
16768          * from the context of the html element.
16769          *
16770          * @TODO figure out what we can do about mouse events lost when the
16771          * user drags objects beyond the window boundary.  Currently we can
16772          * detect this in internet explorer by verifying that the mouse is
16773          * down during the mousemove event.  Firefox doesn't give us the
16774          * button state on the mousemove event.
16775          * @method handleMouseMove
16776          * @param {Event} e the event
16777          * @private
16778          * @static
16779          */
16780         handleMouseMove: function(e) {
16781             if (! this.dragCurrent) {
16782                 return true;
16783             }
16784
16785             // var button = e.which || e.button;
16786
16787             // check for IE mouseup outside of page boundary
16788             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16789                 this.stopEvent(e);
16790                 return this.handleMouseUp(e);
16791             }
16792
16793             if (!this.dragThreshMet) {
16794                 var diffX = Math.abs(this.startX - e.getPageX());
16795                 var diffY = Math.abs(this.startY - e.getPageY());
16796                 if (diffX > this.clickPixelThresh ||
16797                             diffY > this.clickPixelThresh) {
16798                     this.startDrag(this.startX, this.startY);
16799                 }
16800             }
16801
16802             if (this.dragThreshMet) {
16803                 this.dragCurrent.b4Drag(e);
16804                 this.dragCurrent.onDrag(e);
16805                 if(!this.dragCurrent.moveOnly){
16806                     this.fireEvents(e, false);
16807                 }
16808             }
16809
16810             this.stopEvent(e);
16811
16812             return true;
16813         },
16814
16815         /**
16816          * Iterates over all of the DragDrop elements to find ones we are
16817          * hovering over or dropping on
16818          * @method fireEvents
16819          * @param {Event} e the event
16820          * @param {boolean} isDrop is this a drop op or a mouseover op?
16821          * @private
16822          * @static
16823          */
16824         fireEvents: function(e, isDrop) {
16825             var dc = this.dragCurrent;
16826
16827             // If the user did the mouse up outside of the window, we could
16828             // get here even though we have ended the drag.
16829             if (!dc || dc.isLocked()) {
16830                 return;
16831             }
16832
16833             var pt = e.getPoint();
16834
16835             // cache the previous dragOver array
16836             var oldOvers = [];
16837
16838             var outEvts   = [];
16839             var overEvts  = [];
16840             var dropEvts  = [];
16841             var enterEvts = [];
16842
16843             // Check to see if the object(s) we were hovering over is no longer
16844             // being hovered over so we can fire the onDragOut event
16845             for (var i in this.dragOvers) {
16846
16847                 var ddo = this.dragOvers[i];
16848
16849                 if (! this.isTypeOfDD(ddo)) {
16850                     continue;
16851                 }
16852
16853                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16854                     outEvts.push( ddo );
16855                 }
16856
16857                 oldOvers[i] = true;
16858                 delete this.dragOvers[i];
16859             }
16860
16861             for (var sGroup in dc.groups) {
16862
16863                 if ("string" != typeof sGroup) {
16864                     continue;
16865                 }
16866
16867                 for (i in this.ids[sGroup]) {
16868                     var oDD = this.ids[sGroup][i];
16869                     if (! this.isTypeOfDD(oDD)) {
16870                         continue;
16871                     }
16872
16873                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16874                         if (this.isOverTarget(pt, oDD, this.mode)) {
16875                             // look for drop interactions
16876                             if (isDrop) {
16877                                 dropEvts.push( oDD );
16878                             // look for drag enter and drag over interactions
16879                             } else {
16880
16881                                 // initial drag over: dragEnter fires
16882                                 if (!oldOvers[oDD.id]) {
16883                                     enterEvts.push( oDD );
16884                                 // subsequent drag overs: dragOver fires
16885                                 } else {
16886                                     overEvts.push( oDD );
16887                                 }
16888
16889                                 this.dragOvers[oDD.id] = oDD;
16890                             }
16891                         }
16892                     }
16893                 }
16894             }
16895
16896             if (this.mode) {
16897                 if (outEvts.length) {
16898                     dc.b4DragOut(e, outEvts);
16899                     dc.onDragOut(e, outEvts);
16900                 }
16901
16902                 if (enterEvts.length) {
16903                     dc.onDragEnter(e, enterEvts);
16904                 }
16905
16906                 if (overEvts.length) {
16907                     dc.b4DragOver(e, overEvts);
16908                     dc.onDragOver(e, overEvts);
16909                 }
16910
16911                 if (dropEvts.length) {
16912                     dc.b4DragDrop(e, dropEvts);
16913                     dc.onDragDrop(e, dropEvts);
16914                 }
16915
16916             } else {
16917                 // fire dragout events
16918                 var len = 0;
16919                 for (i=0, len=outEvts.length; i<len; ++i) {
16920                     dc.b4DragOut(e, outEvts[i].id);
16921                     dc.onDragOut(e, outEvts[i].id);
16922                 }
16923
16924                 // fire enter events
16925                 for (i=0,len=enterEvts.length; i<len; ++i) {
16926                     // dc.b4DragEnter(e, oDD.id);
16927                     dc.onDragEnter(e, enterEvts[i].id);
16928                 }
16929
16930                 // fire over events
16931                 for (i=0,len=overEvts.length; i<len; ++i) {
16932                     dc.b4DragOver(e, overEvts[i].id);
16933                     dc.onDragOver(e, overEvts[i].id);
16934                 }
16935
16936                 // fire drop events
16937                 for (i=0, len=dropEvts.length; i<len; ++i) {
16938                     dc.b4DragDrop(e, dropEvts[i].id);
16939                     dc.onDragDrop(e, dropEvts[i].id);
16940                 }
16941
16942             }
16943
16944             // notify about a drop that did not find a target
16945             if (isDrop && !dropEvts.length) {
16946                 dc.onInvalidDrop(e);
16947             }
16948
16949         },
16950
16951         /**
16952          * Helper function for getting the best match from the list of drag
16953          * and drop objects returned by the drag and drop events when we are
16954          * in INTERSECT mode.  It returns either the first object that the
16955          * cursor is over, or the object that has the greatest overlap with
16956          * the dragged element.
16957          * @method getBestMatch
16958          * @param  {DragDrop[]} dds The array of drag and drop objects
16959          * targeted
16960          * @return {DragDrop}       The best single match
16961          * @static
16962          */
16963         getBestMatch: function(dds) {
16964             var winner = null;
16965             // Return null if the input is not what we expect
16966             //if (!dds || !dds.length || dds.length == 0) {
16967                // winner = null;
16968             // If there is only one item, it wins
16969             //} else if (dds.length == 1) {
16970
16971             var len = dds.length;
16972
16973             if (len == 1) {
16974                 winner = dds[0];
16975             } else {
16976                 // Loop through the targeted items
16977                 for (var i=0; i<len; ++i) {
16978                     var dd = dds[i];
16979                     // If the cursor is over the object, it wins.  If the
16980                     // cursor is over multiple matches, the first one we come
16981                     // to wins.
16982                     if (dd.cursorIsOver) {
16983                         winner = dd;
16984                         break;
16985                     // Otherwise the object with the most overlap wins
16986                     } else {
16987                         if (!winner ||
16988                             winner.overlap.getArea() < dd.overlap.getArea()) {
16989                             winner = dd;
16990                         }
16991                     }
16992                 }
16993             }
16994
16995             return winner;
16996         },
16997
16998         /**
16999          * Refreshes the cache of the top-left and bottom-right points of the
17000          * drag and drop objects in the specified group(s).  This is in the
17001          * format that is stored in the drag and drop instance, so typical
17002          * usage is:
17003          * <code>
17004          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
17005          * </code>
17006          * Alternatively:
17007          * <code>
17008          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
17009          * </code>
17010          * @TODO this really should be an indexed array.  Alternatively this
17011          * method could accept both.
17012          * @method refreshCache
17013          * @param {Object} groups an associative array of groups to refresh
17014          * @static
17015          */
17016         refreshCache: function(groups) {
17017             for (var sGroup in groups) {
17018                 if ("string" != typeof sGroup) {
17019                     continue;
17020                 }
17021                 for (var i in this.ids[sGroup]) {
17022                     var oDD = this.ids[sGroup][i];
17023
17024                     if (this.isTypeOfDD(oDD)) {
17025                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
17026                         var loc = this.getLocation(oDD);
17027                         if (loc) {
17028                             this.locationCache[oDD.id] = loc;
17029                         } else {
17030                             delete this.locationCache[oDD.id];
17031                             // this will unregister the drag and drop object if
17032                             // the element is not in a usable state
17033                             // oDD.unreg();
17034                         }
17035                     }
17036                 }
17037             }
17038         },
17039
17040         /**
17041          * This checks to make sure an element exists and is in the DOM.  The
17042          * main purpose is to handle cases where innerHTML is used to remove
17043          * drag and drop objects from the DOM.  IE provides an 'unspecified
17044          * error' when trying to access the offsetParent of such an element
17045          * @method verifyEl
17046          * @param {HTMLElement} el the element to check
17047          * @return {boolean} true if the element looks usable
17048          * @static
17049          */
17050         verifyEl: function(el) {
17051             if (el) {
17052                 var parent;
17053                 if(Roo.isIE){
17054                     try{
17055                         parent = el.offsetParent;
17056                     }catch(e){}
17057                 }else{
17058                     parent = el.offsetParent;
17059                 }
17060                 if (parent) {
17061                     return true;
17062                 }
17063             }
17064
17065             return false;
17066         },
17067
17068         /**
17069          * Returns a Region object containing the drag and drop element's position
17070          * and size, including the padding configured for it
17071          * @method getLocation
17072          * @param {DragDrop} oDD the drag and drop object to get the
17073          *                       location for
17074          * @return {Roo.lib.Region} a Region object representing the total area
17075          *                             the element occupies, including any padding
17076          *                             the instance is configured for.
17077          * @static
17078          */
17079         getLocation: function(oDD) {
17080             if (! this.isTypeOfDD(oDD)) {
17081                 return null;
17082             }
17083
17084             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
17085
17086             try {
17087                 pos= Roo.lib.Dom.getXY(el);
17088             } catch (e) { }
17089
17090             if (!pos) {
17091                 return null;
17092             }
17093
17094             x1 = pos[0];
17095             x2 = x1 + el.offsetWidth;
17096             y1 = pos[1];
17097             y2 = y1 + el.offsetHeight;
17098
17099             t = y1 - oDD.padding[0];
17100             r = x2 + oDD.padding[1];
17101             b = y2 + oDD.padding[2];
17102             l = x1 - oDD.padding[3];
17103
17104             return new Roo.lib.Region( t, r, b, l );
17105         },
17106
17107         /**
17108          * Checks the cursor location to see if it over the target
17109          * @method isOverTarget
17110          * @param {Roo.lib.Point} pt The point to evaluate
17111          * @param {DragDrop} oTarget the DragDrop object we are inspecting
17112          * @return {boolean} true if the mouse is over the target
17113          * @private
17114          * @static
17115          */
17116         isOverTarget: function(pt, oTarget, intersect) {
17117             // use cache if available
17118             var loc = this.locationCache[oTarget.id];
17119             if (!loc || !this.useCache) {
17120                 loc = this.getLocation(oTarget);
17121                 this.locationCache[oTarget.id] = loc;
17122
17123             }
17124
17125             if (!loc) {
17126                 return false;
17127             }
17128
17129             oTarget.cursorIsOver = loc.contains( pt );
17130
17131             // DragDrop is using this as a sanity check for the initial mousedown
17132             // in this case we are done.  In POINT mode, if the drag obj has no
17133             // contraints, we are also done. Otherwise we need to evaluate the
17134             // location of the target as related to the actual location of the
17135             // dragged element.
17136             var dc = this.dragCurrent;
17137             if (!dc || !dc.getTargetCoord ||
17138                     (!intersect && !dc.constrainX && !dc.constrainY)) {
17139                 return oTarget.cursorIsOver;
17140             }
17141
17142             oTarget.overlap = null;
17143
17144             // Get the current location of the drag element, this is the
17145             // location of the mouse event less the delta that represents
17146             // where the original mousedown happened on the element.  We
17147             // need to consider constraints and ticks as well.
17148             var pos = dc.getTargetCoord(pt.x, pt.y);
17149
17150             var el = dc.getDragEl();
17151             var curRegion = new Roo.lib.Region( pos.y,
17152                                                    pos.x + el.offsetWidth,
17153                                                    pos.y + el.offsetHeight,
17154                                                    pos.x );
17155
17156             var overlap = curRegion.intersect(loc);
17157
17158             if (overlap) {
17159                 oTarget.overlap = overlap;
17160                 return (intersect) ? true : oTarget.cursorIsOver;
17161             } else {
17162                 return false;
17163             }
17164         },
17165
17166         /**
17167          * unload event handler
17168          * @method _onUnload
17169          * @private
17170          * @static
17171          */
17172         _onUnload: function(e, me) {
17173             Roo.dd.DragDropMgr.unregAll();
17174         },
17175
17176         /**
17177          * Cleans up the drag and drop events and objects.
17178          * @method unregAll
17179          * @private
17180          * @static
17181          */
17182         unregAll: function() {
17183
17184             if (this.dragCurrent) {
17185                 this.stopDrag();
17186                 this.dragCurrent = null;
17187             }
17188
17189             this._execOnAll("unreg", []);
17190
17191             for (i in this.elementCache) {
17192                 delete this.elementCache[i];
17193             }
17194
17195             this.elementCache = {};
17196             this.ids = {};
17197         },
17198
17199         /**
17200          * A cache of DOM elements
17201          * @property elementCache
17202          * @private
17203          * @static
17204          */
17205         elementCache: {},
17206
17207         /**
17208          * Get the wrapper for the DOM element specified
17209          * @method getElWrapper
17210          * @param {String} id the id of the element to get
17211          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
17212          * @private
17213          * @deprecated This wrapper isn't that useful
17214          * @static
17215          */
17216         getElWrapper: function(id) {
17217             var oWrapper = this.elementCache[id];
17218             if (!oWrapper || !oWrapper.el) {
17219                 oWrapper = this.elementCache[id] =
17220                     new this.ElementWrapper(Roo.getDom(id));
17221             }
17222             return oWrapper;
17223         },
17224
17225         /**
17226          * Returns the actual DOM element
17227          * @method getElement
17228          * @param {String} id the id of the elment to get
17229          * @return {Object} The element
17230          * @deprecated use Roo.getDom instead
17231          * @static
17232          */
17233         getElement: function(id) {
17234             return Roo.getDom(id);
17235         },
17236
17237         /**
17238          * Returns the style property for the DOM element (i.e.,
17239          * document.getElById(id).style)
17240          * @method getCss
17241          * @param {String} id the id of the elment to get
17242          * @return {Object} The style property of the element
17243          * @deprecated use Roo.getDom instead
17244          * @static
17245          */
17246         getCss: function(id) {
17247             var el = Roo.getDom(id);
17248             return (el) ? el.style : null;
17249         },
17250
17251         /**
17252          * Inner class for cached elements
17253          * @class DragDropMgr.ElementWrapper
17254          * @for DragDropMgr
17255          * @private
17256          * @deprecated
17257          */
17258         ElementWrapper: function(el) {
17259                 /**
17260                  * The element
17261                  * @property el
17262                  */
17263                 this.el = el || null;
17264                 /**
17265                  * The element id
17266                  * @property id
17267                  */
17268                 this.id = this.el && el.id;
17269                 /**
17270                  * A reference to the style property
17271                  * @property css
17272                  */
17273                 this.css = this.el && el.style;
17274             },
17275
17276         /**
17277          * Returns the X position of an html element
17278          * @method getPosX
17279          * @param el the element for which to get the position
17280          * @return {int} the X coordinate
17281          * @for DragDropMgr
17282          * @deprecated use Roo.lib.Dom.getX instead
17283          * @static
17284          */
17285         getPosX: function(el) {
17286             return Roo.lib.Dom.getX(el);
17287         },
17288
17289         /**
17290          * Returns the Y position of an html element
17291          * @method getPosY
17292          * @param el the element for which to get the position
17293          * @return {int} the Y coordinate
17294          * @deprecated use Roo.lib.Dom.getY instead
17295          * @static
17296          */
17297         getPosY: function(el) {
17298             return Roo.lib.Dom.getY(el);
17299         },
17300
17301         /**
17302          * Swap two nodes.  In IE, we use the native method, for others we
17303          * emulate the IE behavior
17304          * @method swapNode
17305          * @param n1 the first node to swap
17306          * @param n2 the other node to swap
17307          * @static
17308          */
17309         swapNode: function(n1, n2) {
17310             if (n1.swapNode) {
17311                 n1.swapNode(n2);
17312             } else {
17313                 var p = n2.parentNode;
17314                 var s = n2.nextSibling;
17315
17316                 if (s == n1) {
17317                     p.insertBefore(n1, n2);
17318                 } else if (n2 == n1.nextSibling) {
17319                     p.insertBefore(n2, n1);
17320                 } else {
17321                     n1.parentNode.replaceChild(n2, n1);
17322                     p.insertBefore(n1, s);
17323                 }
17324             }
17325         },
17326
17327         /**
17328          * Returns the current scroll position
17329          * @method getScroll
17330          * @private
17331          * @static
17332          */
17333         getScroll: function () {
17334             var t, l, dde=document.documentElement, db=document.body;
17335             if (dde && (dde.scrollTop || dde.scrollLeft)) {
17336                 t = dde.scrollTop;
17337                 l = dde.scrollLeft;
17338             } else if (db) {
17339                 t = db.scrollTop;
17340                 l = db.scrollLeft;
17341             } else {
17342
17343             }
17344             return { top: t, left: l };
17345         },
17346
17347         /**
17348          * Returns the specified element style property
17349          * @method getStyle
17350          * @param {HTMLElement} el          the element
17351          * @param {string}      styleProp   the style property
17352          * @return {string} The value of the style property
17353          * @deprecated use Roo.lib.Dom.getStyle
17354          * @static
17355          */
17356         getStyle: function(el, styleProp) {
17357             return Roo.fly(el).getStyle(styleProp);
17358         },
17359
17360         /**
17361          * Gets the scrollTop
17362          * @method getScrollTop
17363          * @return {int} the document's scrollTop
17364          * @static
17365          */
17366         getScrollTop: function () { return this.getScroll().top; },
17367
17368         /**
17369          * Gets the scrollLeft
17370          * @method getScrollLeft
17371          * @return {int} the document's scrollTop
17372          * @static
17373          */
17374         getScrollLeft: function () { return this.getScroll().left; },
17375
17376         /**
17377          * Sets the x/y position of an element to the location of the
17378          * target element.
17379          * @method moveToEl
17380          * @param {HTMLElement} moveEl      The element to move
17381          * @param {HTMLElement} targetEl    The position reference element
17382          * @static
17383          */
17384         moveToEl: function (moveEl, targetEl) {
17385             var aCoord = Roo.lib.Dom.getXY(targetEl);
17386             Roo.lib.Dom.setXY(moveEl, aCoord);
17387         },
17388
17389         /**
17390          * Numeric array sort function
17391          * @method numericSort
17392          * @static
17393          */
17394         numericSort: function(a, b) { return (a - b); },
17395
17396         /**
17397          * Internal counter
17398          * @property _timeoutCount
17399          * @private
17400          * @static
17401          */
17402         _timeoutCount: 0,
17403
17404         /**
17405          * Trying to make the load order less important.  Without this we get
17406          * an error if this file is loaded before the Event Utility.
17407          * @method _addListeners
17408          * @private
17409          * @static
17410          */
17411         _addListeners: function() {
17412             var DDM = Roo.dd.DDM;
17413             if ( Roo.lib.Event && document ) {
17414                 DDM._onLoad();
17415             } else {
17416                 if (DDM._timeoutCount > 2000) {
17417                 } else {
17418                     setTimeout(DDM._addListeners, 10);
17419                     if (document && document.body) {
17420                         DDM._timeoutCount += 1;
17421                     }
17422                 }
17423             }
17424         },
17425
17426         /**
17427          * Recursively searches the immediate parent and all child nodes for
17428          * the handle element in order to determine wheter or not it was
17429          * clicked.
17430          * @method handleWasClicked
17431          * @param node the html element to inspect
17432          * @static
17433          */
17434         handleWasClicked: function(node, id) {
17435             if (this.isHandle(id, node.id)) {
17436                 return true;
17437             } else {
17438                 // check to see if this is a text node child of the one we want
17439                 var p = node.parentNode;
17440
17441                 while (p) {
17442                     if (this.isHandle(id, p.id)) {
17443                         return true;
17444                     } else {
17445                         p = p.parentNode;
17446                     }
17447                 }
17448             }
17449
17450             return false;
17451         }
17452
17453     };
17454
17455 }();
17456
17457 // shorter alias, save a few bytes
17458 Roo.dd.DDM = Roo.dd.DragDropMgr;
17459 Roo.dd.DDM._addListeners();
17460
17461 }/*
17462  * Based on:
17463  * Ext JS Library 1.1.1
17464  * Copyright(c) 2006-2007, Ext JS, LLC.
17465  *
17466  * Originally Released Under LGPL - original licence link has changed is not relivant.
17467  *
17468  * Fork - LGPL
17469  * <script type="text/javascript">
17470  */
17471
17472 /**
17473  * @class Roo.dd.DD
17474  * A DragDrop implementation where the linked element follows the
17475  * mouse cursor during a drag.
17476  * @extends Roo.dd.DragDrop
17477  * @constructor
17478  * @param {String} id the id of the linked element
17479  * @param {String} sGroup the group of related DragDrop items
17480  * @param {object} config an object containing configurable attributes
17481  *                Valid properties for DD:
17482  *                    scroll
17483  */
17484 Roo.dd.DD = function(id, sGroup, config) {
17485     if (id) {
17486         this.init(id, sGroup, config);
17487     }
17488 };
17489
17490 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
17491
17492     /**
17493      * When set to true, the utility automatically tries to scroll the browser
17494      * window wehn a drag and drop element is dragged near the viewport boundary.
17495      * Defaults to true.
17496      * @property scroll
17497      * @type boolean
17498      */
17499     scroll: true,
17500
17501     /**
17502      * Sets the pointer offset to the distance between the linked element's top
17503      * left corner and the location the element was clicked
17504      * @method autoOffset
17505      * @param {int} iPageX the X coordinate of the click
17506      * @param {int} iPageY the Y coordinate of the click
17507      */
17508     autoOffset: function(iPageX, iPageY) {
17509         var x = iPageX - this.startPageX;
17510         var y = iPageY - this.startPageY;
17511         this.setDelta(x, y);
17512     },
17513
17514     /**
17515      * Sets the pointer offset.  You can call this directly to force the
17516      * offset to be in a particular location (e.g., pass in 0,0 to set it
17517      * to the center of the object)
17518      * @method setDelta
17519      * @param {int} iDeltaX the distance from the left
17520      * @param {int} iDeltaY the distance from the top
17521      */
17522     setDelta: function(iDeltaX, iDeltaY) {
17523         this.deltaX = iDeltaX;
17524         this.deltaY = iDeltaY;
17525     },
17526
17527     /**
17528      * Sets the drag element to the location of the mousedown or click event,
17529      * maintaining the cursor location relative to the location on the element
17530      * that was clicked.  Override this if you want to place the element in a
17531      * location other than where the cursor is.
17532      * @method setDragElPos
17533      * @param {int} iPageX the X coordinate of the mousedown or drag event
17534      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17535      */
17536     setDragElPos: function(iPageX, iPageY) {
17537         // the first time we do this, we are going to check to make sure
17538         // the element has css positioning
17539
17540         var el = this.getDragEl();
17541         this.alignElWithMouse(el, iPageX, iPageY);
17542     },
17543
17544     /**
17545      * Sets the element to the location of the mousedown or click event,
17546      * maintaining the cursor location relative to the location on the element
17547      * that was clicked.  Override this if you want to place the element in a
17548      * location other than where the cursor is.
17549      * @method alignElWithMouse
17550      * @param {HTMLElement} el the element to move
17551      * @param {int} iPageX the X coordinate of the mousedown or drag event
17552      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17553      */
17554     alignElWithMouse: function(el, iPageX, iPageY) {
17555         var oCoord = this.getTargetCoord(iPageX, iPageY);
17556         var fly = el.dom ? el : Roo.fly(el);
17557         if (!this.deltaSetXY) {
17558             var aCoord = [oCoord.x, oCoord.y];
17559             fly.setXY(aCoord);
17560             var newLeft = fly.getLeft(true);
17561             var newTop  = fly.getTop(true);
17562             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17563         } else {
17564             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17565         }
17566
17567         this.cachePosition(oCoord.x, oCoord.y);
17568         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17569         return oCoord;
17570     },
17571
17572     /**
17573      * Saves the most recent position so that we can reset the constraints and
17574      * tick marks on-demand.  We need to know this so that we can calculate the
17575      * number of pixels the element is offset from its original position.
17576      * @method cachePosition
17577      * @param iPageX the current x position (optional, this just makes it so we
17578      * don't have to look it up again)
17579      * @param iPageY the current y position (optional, this just makes it so we
17580      * don't have to look it up again)
17581      */
17582     cachePosition: function(iPageX, iPageY) {
17583         if (iPageX) {
17584             this.lastPageX = iPageX;
17585             this.lastPageY = iPageY;
17586         } else {
17587             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17588             this.lastPageX = aCoord[0];
17589             this.lastPageY = aCoord[1];
17590         }
17591     },
17592
17593     /**
17594      * Auto-scroll the window if the dragged object has been moved beyond the
17595      * visible window boundary.
17596      * @method autoScroll
17597      * @param {int} x the drag element's x position
17598      * @param {int} y the drag element's y position
17599      * @param {int} h the height of the drag element
17600      * @param {int} w the width of the drag element
17601      * @private
17602      */
17603     autoScroll: function(x, y, h, w) {
17604
17605         if (this.scroll) {
17606             // The client height
17607             var clientH = Roo.lib.Dom.getViewWidth();
17608
17609             // The client width
17610             var clientW = Roo.lib.Dom.getViewHeight();
17611
17612             // The amt scrolled down
17613             var st = this.DDM.getScrollTop();
17614
17615             // The amt scrolled right
17616             var sl = this.DDM.getScrollLeft();
17617
17618             // Location of the bottom of the element
17619             var bot = h + y;
17620
17621             // Location of the right of the element
17622             var right = w + x;
17623
17624             // The distance from the cursor to the bottom of the visible area,
17625             // adjusted so that we don't scroll if the cursor is beyond the
17626             // element drag constraints
17627             var toBot = (clientH + st - y - this.deltaY);
17628
17629             // The distance from the cursor to the right of the visible area
17630             var toRight = (clientW + sl - x - this.deltaX);
17631
17632
17633             // How close to the edge the cursor must be before we scroll
17634             // var thresh = (document.all) ? 100 : 40;
17635             var thresh = 40;
17636
17637             // How many pixels to scroll per autoscroll op.  This helps to reduce
17638             // clunky scrolling. IE is more sensitive about this ... it needs this
17639             // value to be higher.
17640             var scrAmt = (document.all) ? 80 : 30;
17641
17642             // Scroll down if we are near the bottom of the visible page and the
17643             // obj extends below the crease
17644             if ( bot > clientH && toBot < thresh ) {
17645                 window.scrollTo(sl, st + scrAmt);
17646             }
17647
17648             // Scroll up if the window is scrolled down and the top of the object
17649             // goes above the top border
17650             if ( y < st && st > 0 && y - st < thresh ) {
17651                 window.scrollTo(sl, st - scrAmt);
17652             }
17653
17654             // Scroll right if the obj is beyond the right border and the cursor is
17655             // near the border.
17656             if ( right > clientW && toRight < thresh ) {
17657                 window.scrollTo(sl + scrAmt, st);
17658             }
17659
17660             // Scroll left if the window has been scrolled to the right and the obj
17661             // extends past the left border
17662             if ( x < sl && sl > 0 && x - sl < thresh ) {
17663                 window.scrollTo(sl - scrAmt, st);
17664             }
17665         }
17666     },
17667
17668     /**
17669      * Finds the location the element should be placed if we want to move
17670      * it to where the mouse location less the click offset would place us.
17671      * @method getTargetCoord
17672      * @param {int} iPageX the X coordinate of the click
17673      * @param {int} iPageY the Y coordinate of the click
17674      * @return an object that contains the coordinates (Object.x and Object.y)
17675      * @private
17676      */
17677     getTargetCoord: function(iPageX, iPageY) {
17678
17679
17680         var x = iPageX - this.deltaX;
17681         var y = iPageY - this.deltaY;
17682
17683         if (this.constrainX) {
17684             if (x < this.minX) { x = this.minX; }
17685             if (x > this.maxX) { x = this.maxX; }
17686         }
17687
17688         if (this.constrainY) {
17689             if (y < this.minY) { y = this.minY; }
17690             if (y > this.maxY) { y = this.maxY; }
17691         }
17692
17693         x = this.getTick(x, this.xTicks);
17694         y = this.getTick(y, this.yTicks);
17695
17696
17697         return {x:x, y:y};
17698     },
17699
17700     /*
17701      * Sets up config options specific to this class. Overrides
17702      * Roo.dd.DragDrop, but all versions of this method through the
17703      * inheritance chain are called
17704      */
17705     applyConfig: function() {
17706         Roo.dd.DD.superclass.applyConfig.call(this);
17707         this.scroll = (this.config.scroll !== false);
17708     },
17709
17710     /*
17711      * Event that fires prior to the onMouseDown event.  Overrides
17712      * Roo.dd.DragDrop.
17713      */
17714     b4MouseDown: function(e) {
17715         // this.resetConstraints();
17716         this.autoOffset(e.getPageX(),
17717                             e.getPageY());
17718     },
17719
17720     /*
17721      * Event that fires prior to the onDrag event.  Overrides
17722      * Roo.dd.DragDrop.
17723      */
17724     b4Drag: function(e) {
17725         this.setDragElPos(e.getPageX(),
17726                             e.getPageY());
17727     },
17728
17729     toString: function() {
17730         return ("DD " + this.id);
17731     }
17732
17733     //////////////////////////////////////////////////////////////////////////
17734     // Debugging ygDragDrop events that can be overridden
17735     //////////////////////////////////////////////////////////////////////////
17736     /*
17737     startDrag: function(x, y) {
17738     },
17739
17740     onDrag: function(e) {
17741     },
17742
17743     onDragEnter: function(e, id) {
17744     },
17745
17746     onDragOver: function(e, id) {
17747     },
17748
17749     onDragOut: function(e, id) {
17750     },
17751
17752     onDragDrop: function(e, id) {
17753     },
17754
17755     endDrag: function(e) {
17756     }
17757
17758     */
17759
17760 });/*
17761  * Based on:
17762  * Ext JS Library 1.1.1
17763  * Copyright(c) 2006-2007, Ext JS, LLC.
17764  *
17765  * Originally Released Under LGPL - original licence link has changed is not relivant.
17766  *
17767  * Fork - LGPL
17768  * <script type="text/javascript">
17769  */
17770
17771 /**
17772  * @class Roo.dd.DDProxy
17773  * A DragDrop implementation that inserts an empty, bordered div into
17774  * the document that follows the cursor during drag operations.  At the time of
17775  * the click, the frame div is resized to the dimensions of the linked html
17776  * element, and moved to the exact location of the linked element.
17777  *
17778  * References to the "frame" element refer to the single proxy element that
17779  * was created to be dragged in place of all DDProxy elements on the
17780  * page.
17781  *
17782  * @extends Roo.dd.DD
17783  * @constructor
17784  * @param {String} id the id of the linked html element
17785  * @param {String} sGroup the group of related DragDrop objects
17786  * @param {object} config an object containing configurable attributes
17787  *                Valid properties for DDProxy in addition to those in DragDrop:
17788  *                   resizeFrame, centerFrame, dragElId
17789  */
17790 Roo.dd.DDProxy = function(id, sGroup, config) {
17791     if (id) {
17792         this.init(id, sGroup, config);
17793         this.initFrame();
17794     }
17795 };
17796
17797 /**
17798  * The default drag frame div id
17799  * @property Roo.dd.DDProxy.dragElId
17800  * @type String
17801  * @static
17802  */
17803 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17804
17805 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17806
17807     /**
17808      * By default we resize the drag frame to be the same size as the element
17809      * we want to drag (this is to get the frame effect).  We can turn it off
17810      * if we want a different behavior.
17811      * @property resizeFrame
17812      * @type boolean
17813      */
17814     resizeFrame: true,
17815
17816     /**
17817      * By default the frame is positioned exactly where the drag element is, so
17818      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17819      * you do not have constraints on the obj is to have the drag frame centered
17820      * around the cursor.  Set centerFrame to true for this effect.
17821      * @property centerFrame
17822      * @type boolean
17823      */
17824     centerFrame: false,
17825
17826     /**
17827      * Creates the proxy element if it does not yet exist
17828      * @method createFrame
17829      */
17830     createFrame: function() {
17831         var self = this;
17832         var body = document.body;
17833
17834         if (!body || !body.firstChild) {
17835             setTimeout( function() { self.createFrame(); }, 50 );
17836             return;
17837         }
17838
17839         var div = this.getDragEl();
17840
17841         if (!div) {
17842             div    = document.createElement("div");
17843             div.id = this.dragElId;
17844             var s  = div.style;
17845
17846             s.position   = "absolute";
17847             s.visibility = "hidden";
17848             s.cursor     = "move";
17849             s.border     = "2px solid #aaa";
17850             s.zIndex     = 999;
17851
17852             // appendChild can blow up IE if invoked prior to the window load event
17853             // while rendering a table.  It is possible there are other scenarios
17854             // that would cause this to happen as well.
17855             body.insertBefore(div, body.firstChild);
17856         }
17857     },
17858
17859     /**
17860      * Initialization for the drag frame element.  Must be called in the
17861      * constructor of all subclasses
17862      * @method initFrame
17863      */
17864     initFrame: function() {
17865         this.createFrame();
17866     },
17867
17868     applyConfig: function() {
17869         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17870
17871         this.resizeFrame = (this.config.resizeFrame !== false);
17872         this.centerFrame = (this.config.centerFrame);
17873         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17874     },
17875
17876     /**
17877      * Resizes the drag frame to the dimensions of the clicked object, positions
17878      * it over the object, and finally displays it
17879      * @method showFrame
17880      * @param {int} iPageX X click position
17881      * @param {int} iPageY Y click position
17882      * @private
17883      */
17884     showFrame: function(iPageX, iPageY) {
17885         var el = this.getEl();
17886         var dragEl = this.getDragEl();
17887         var s = dragEl.style;
17888
17889         this._resizeProxy();
17890
17891         if (this.centerFrame) {
17892             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17893                            Math.round(parseInt(s.height, 10)/2) );
17894         }
17895
17896         this.setDragElPos(iPageX, iPageY);
17897
17898         Roo.fly(dragEl).show();
17899     },
17900
17901     /**
17902      * The proxy is automatically resized to the dimensions of the linked
17903      * element when a drag is initiated, unless resizeFrame is set to false
17904      * @method _resizeProxy
17905      * @private
17906      */
17907     _resizeProxy: function() {
17908         if (this.resizeFrame) {
17909             var el = this.getEl();
17910             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17911         }
17912     },
17913
17914     // overrides Roo.dd.DragDrop
17915     b4MouseDown: function(e) {
17916         var x = e.getPageX();
17917         var y = e.getPageY();
17918         this.autoOffset(x, y);
17919         this.setDragElPos(x, y);
17920     },
17921
17922     // overrides Roo.dd.DragDrop
17923     b4StartDrag: function(x, y) {
17924         // show the drag frame
17925         this.showFrame(x, y);
17926     },
17927
17928     // overrides Roo.dd.DragDrop
17929     b4EndDrag: function(e) {
17930         Roo.fly(this.getDragEl()).hide();
17931     },
17932
17933     // overrides Roo.dd.DragDrop
17934     // By default we try to move the element to the last location of the frame.
17935     // This is so that the default behavior mirrors that of Roo.dd.DD.
17936     endDrag: function(e) {
17937
17938         var lel = this.getEl();
17939         var del = this.getDragEl();
17940
17941         // Show the drag frame briefly so we can get its position
17942         del.style.visibility = "";
17943
17944         this.beforeMove();
17945         // Hide the linked element before the move to get around a Safari
17946         // rendering bug.
17947         lel.style.visibility = "hidden";
17948         Roo.dd.DDM.moveToEl(lel, del);
17949         del.style.visibility = "hidden";
17950         lel.style.visibility = "";
17951
17952         this.afterDrag();
17953     },
17954
17955     beforeMove : function(){
17956
17957     },
17958
17959     afterDrag : function(){
17960
17961     },
17962
17963     toString: function() {
17964         return ("DDProxy " + this.id);
17965     }
17966
17967 });
17968 /*
17969  * Based on:
17970  * Ext JS Library 1.1.1
17971  * Copyright(c) 2006-2007, Ext JS, LLC.
17972  *
17973  * Originally Released Under LGPL - original licence link has changed is not relivant.
17974  *
17975  * Fork - LGPL
17976  * <script type="text/javascript">
17977  */
17978
17979  /**
17980  * @class Roo.dd.DDTarget
17981  * A DragDrop implementation that does not move, but can be a drop
17982  * target.  You would get the same result by simply omitting implementation
17983  * for the event callbacks, but this way we reduce the processing cost of the
17984  * event listener and the callbacks.
17985  * @extends Roo.dd.DragDrop
17986  * @constructor
17987  * @param {String} id the id of the element that is a drop target
17988  * @param {String} sGroup the group of related DragDrop objects
17989  * @param {object} config an object containing configurable attributes
17990  *                 Valid properties for DDTarget in addition to those in
17991  *                 DragDrop:
17992  *                    none
17993  */
17994 Roo.dd.DDTarget = function(id, sGroup, config) {
17995     if (id) {
17996         this.initTarget(id, sGroup, config);
17997     }
17998     if (config.listeners || config.events) { 
17999        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
18000             listeners : config.listeners || {}, 
18001             events : config.events || {} 
18002         });    
18003     }
18004 };
18005
18006 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
18007 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
18008     toString: function() {
18009         return ("DDTarget " + this.id);
18010     }
18011 });
18012 /*
18013  * Based on:
18014  * Ext JS Library 1.1.1
18015  * Copyright(c) 2006-2007, Ext JS, LLC.
18016  *
18017  * Originally Released Under LGPL - original licence link has changed is not relivant.
18018  *
18019  * Fork - LGPL
18020  * <script type="text/javascript">
18021  */
18022  
18023
18024 /**
18025  * @class Roo.dd.ScrollManager
18026  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
18027  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
18028  * @singleton
18029  */
18030 Roo.dd.ScrollManager = function(){
18031     var ddm = Roo.dd.DragDropMgr;
18032     var els = {};
18033     var dragEl = null;
18034     var proc = {};
18035     
18036     
18037     
18038     var onStop = function(e){
18039         dragEl = null;
18040         clearProc();
18041     };
18042     
18043     var triggerRefresh = function(){
18044         if(ddm.dragCurrent){
18045              ddm.refreshCache(ddm.dragCurrent.groups);
18046         }
18047     };
18048     
18049     var doScroll = function(){
18050         if(ddm.dragCurrent){
18051             var dds = Roo.dd.ScrollManager;
18052             if(!dds.animate){
18053                 if(proc.el.scroll(proc.dir, dds.increment)){
18054                     triggerRefresh();
18055                 }
18056             }else{
18057                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
18058             }
18059         }
18060     };
18061     
18062     var clearProc = function(){
18063         if(proc.id){
18064             clearInterval(proc.id);
18065         }
18066         proc.id = 0;
18067         proc.el = null;
18068         proc.dir = "";
18069     };
18070     
18071     var startProc = function(el, dir){
18072          Roo.log('scroll startproc');
18073         clearProc();
18074         proc.el = el;
18075         proc.dir = dir;
18076         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
18077     };
18078     
18079     var onFire = function(e, isDrop){
18080        
18081         if(isDrop || !ddm.dragCurrent){ return; }
18082         var dds = Roo.dd.ScrollManager;
18083         if(!dragEl || dragEl != ddm.dragCurrent){
18084             dragEl = ddm.dragCurrent;
18085             // refresh regions on drag start
18086             dds.refreshCache();
18087         }
18088         
18089         var xy = Roo.lib.Event.getXY(e);
18090         var pt = new Roo.lib.Point(xy[0], xy[1]);
18091         for(var id in els){
18092             var el = els[id], r = el._region;
18093             if(r && r.contains(pt) && el.isScrollable()){
18094                 if(r.bottom - pt.y <= dds.thresh){
18095                     if(proc.el != el){
18096                         startProc(el, "down");
18097                     }
18098                     return;
18099                 }else if(r.right - pt.x <= dds.thresh){
18100                     if(proc.el != el){
18101                         startProc(el, "left");
18102                     }
18103                     return;
18104                 }else if(pt.y - r.top <= dds.thresh){
18105                     if(proc.el != el){
18106                         startProc(el, "up");
18107                     }
18108                     return;
18109                 }else if(pt.x - r.left <= dds.thresh){
18110                     if(proc.el != el){
18111                         startProc(el, "right");
18112                     }
18113                     return;
18114                 }
18115             }
18116         }
18117         clearProc();
18118     };
18119     
18120     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
18121     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
18122     
18123     return {
18124         /**
18125          * Registers new overflow element(s) to auto scroll
18126          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
18127          */
18128         register : function(el){
18129             if(el instanceof Array){
18130                 for(var i = 0, len = el.length; i < len; i++) {
18131                         this.register(el[i]);
18132                 }
18133             }else{
18134                 el = Roo.get(el);
18135                 els[el.id] = el;
18136             }
18137             Roo.dd.ScrollManager.els = els;
18138         },
18139         
18140         /**
18141          * Unregisters overflow element(s) so they are no longer scrolled
18142          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
18143          */
18144         unregister : function(el){
18145             if(el instanceof Array){
18146                 for(var i = 0, len = el.length; i < len; i++) {
18147                         this.unregister(el[i]);
18148                 }
18149             }else{
18150                 el = Roo.get(el);
18151                 delete els[el.id];
18152             }
18153         },
18154         
18155         /**
18156          * The number of pixels from the edge of a container the pointer needs to be to 
18157          * trigger scrolling (defaults to 25)
18158          * @type Number
18159          */
18160         thresh : 25,
18161         
18162         /**
18163          * The number of pixels to scroll in each scroll increment (defaults to 50)
18164          * @type Number
18165          */
18166         increment : 100,
18167         
18168         /**
18169          * The frequency of scrolls in milliseconds (defaults to 500)
18170          * @type Number
18171          */
18172         frequency : 500,
18173         
18174         /**
18175          * True to animate the scroll (defaults to true)
18176          * @type Boolean
18177          */
18178         animate: true,
18179         
18180         /**
18181          * The animation duration in seconds - 
18182          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
18183          * @type Number
18184          */
18185         animDuration: .4,
18186         
18187         /**
18188          * Manually trigger a cache refresh.
18189          */
18190         refreshCache : function(){
18191             for(var id in els){
18192                 if(typeof els[id] == 'object'){ // for people extending the object prototype
18193                     els[id]._region = els[id].getRegion();
18194                 }
18195             }
18196         }
18197     };
18198 }();/*
18199  * Based on:
18200  * Ext JS Library 1.1.1
18201  * Copyright(c) 2006-2007, Ext JS, LLC.
18202  *
18203  * Originally Released Under LGPL - original licence link has changed is not relivant.
18204  *
18205  * Fork - LGPL
18206  * <script type="text/javascript">
18207  */
18208  
18209
18210 /**
18211  * @class Roo.dd.Registry
18212  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
18213  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
18214  * @singleton
18215  */
18216 Roo.dd.Registry = function(){
18217     var elements = {}; 
18218     var handles = {}; 
18219     var autoIdSeed = 0;
18220
18221     var getId = function(el, autogen){
18222         if(typeof el == "string"){
18223             return el;
18224         }
18225         var id = el.id;
18226         if(!id && autogen !== false){
18227             id = "roodd-" + (++autoIdSeed);
18228             el.id = id;
18229         }
18230         return id;
18231     };
18232     
18233     return {
18234     /**
18235      * Register a drag drop element
18236      * @param {String|HTMLElement} element The id or DOM node to register
18237      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
18238      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
18239      * knows how to interpret, plus there are some specific properties known to the Registry that should be
18240      * populated in the data object (if applicable):
18241      * <pre>
18242 Value      Description<br />
18243 ---------  ------------------------------------------<br />
18244 handles    Array of DOM nodes that trigger dragging<br />
18245            for the element being registered<br />
18246 isHandle   True if the element passed in triggers<br />
18247            dragging itself, else false
18248 </pre>
18249      */
18250         register : function(el, data){
18251             data = data || {};
18252             if(typeof el == "string"){
18253                 el = document.getElementById(el);
18254             }
18255             data.ddel = el;
18256             elements[getId(el)] = data;
18257             if(data.isHandle !== false){
18258                 handles[data.ddel.id] = data;
18259             }
18260             if(data.handles){
18261                 var hs = data.handles;
18262                 for(var i = 0, len = hs.length; i < len; i++){
18263                         handles[getId(hs[i])] = data;
18264                 }
18265             }
18266         },
18267
18268     /**
18269      * Unregister a drag drop element
18270      * @param {String|HTMLElement}  element The id or DOM node to unregister
18271      */
18272         unregister : function(el){
18273             var id = getId(el, false);
18274             var data = elements[id];
18275             if(data){
18276                 delete elements[id];
18277                 if(data.handles){
18278                     var hs = data.handles;
18279                     for(var i = 0, len = hs.length; i < len; i++){
18280                         delete handles[getId(hs[i], false)];
18281                     }
18282                 }
18283             }
18284         },
18285
18286     /**
18287      * Returns the handle registered for a DOM Node by id
18288      * @param {String|HTMLElement} id The DOM node or id to look up
18289      * @return {Object} handle The custom handle data
18290      */
18291         getHandle : function(id){
18292             if(typeof id != "string"){ // must be element?
18293                 id = id.id;
18294             }
18295             return handles[id];
18296         },
18297
18298     /**
18299      * Returns the handle that is registered for the DOM node that is the target of the event
18300      * @param {Event} e The event
18301      * @return {Object} handle The custom handle data
18302      */
18303         getHandleFromEvent : function(e){
18304             var t = Roo.lib.Event.getTarget(e);
18305             return t ? handles[t.id] : null;
18306         },
18307
18308     /**
18309      * Returns a custom data object that is registered for a DOM node by id
18310      * @param {String|HTMLElement} id The DOM node or id to look up
18311      * @return {Object} data The custom data
18312      */
18313         getTarget : function(id){
18314             if(typeof id != "string"){ // must be element?
18315                 id = id.id;
18316             }
18317             return elements[id];
18318         },
18319
18320     /**
18321      * Returns a custom data object that is registered for the DOM node that is the target of the event
18322      * @param {Event} e The event
18323      * @return {Object} data The custom data
18324      */
18325         getTargetFromEvent : function(e){
18326             var t = Roo.lib.Event.getTarget(e);
18327             return t ? elements[t.id] || handles[t.id] : null;
18328         }
18329     };
18330 }();/*
18331  * Based on:
18332  * Ext JS Library 1.1.1
18333  * Copyright(c) 2006-2007, Ext JS, LLC.
18334  *
18335  * Originally Released Under LGPL - original licence link has changed is not relivant.
18336  *
18337  * Fork - LGPL
18338  * <script type="text/javascript">
18339  */
18340  
18341
18342 /**
18343  * @class Roo.dd.StatusProxy
18344  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
18345  * default drag proxy used by all Roo.dd components.
18346  * @constructor
18347  * @param {Object} config
18348  */
18349 Roo.dd.StatusProxy = function(config){
18350     Roo.apply(this, config);
18351     this.id = this.id || Roo.id();
18352     this.el = new Roo.Layer({
18353         dh: {
18354             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
18355                 {tag: "div", cls: "x-dd-drop-icon"},
18356                 {tag: "div", cls: "x-dd-drag-ghost"}
18357             ]
18358         }, 
18359         shadow: !config || config.shadow !== false
18360     });
18361     this.ghost = Roo.get(this.el.dom.childNodes[1]);
18362     this.dropStatus = this.dropNotAllowed;
18363 };
18364
18365 Roo.dd.StatusProxy.prototype = {
18366     /**
18367      * @cfg {String} dropAllowed
18368      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
18369      */
18370     dropAllowed : "x-dd-drop-ok",
18371     /**
18372      * @cfg {String} dropNotAllowed
18373      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
18374      */
18375     dropNotAllowed : "x-dd-drop-nodrop",
18376
18377     /**
18378      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
18379      * over the current target element.
18380      * @param {String} cssClass The css class for the new drop status indicator image
18381      */
18382     setStatus : function(cssClass){
18383         cssClass = cssClass || this.dropNotAllowed;
18384         if(this.dropStatus != cssClass){
18385             this.el.replaceClass(this.dropStatus, cssClass);
18386             this.dropStatus = cssClass;
18387         }
18388     },
18389
18390     /**
18391      * Resets the status indicator to the default dropNotAllowed value
18392      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
18393      */
18394     reset : function(clearGhost){
18395         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
18396         this.dropStatus = this.dropNotAllowed;
18397         if(clearGhost){
18398             this.ghost.update("");
18399         }
18400     },
18401
18402     /**
18403      * Updates the contents of the ghost element
18404      * @param {String} html The html that will replace the current innerHTML of the ghost element
18405      */
18406     update : function(html){
18407         if(typeof html == "string"){
18408             this.ghost.update(html);
18409         }else{
18410             this.ghost.update("");
18411             html.style.margin = "0";
18412             this.ghost.dom.appendChild(html);
18413         }
18414         // ensure float = none set?? cant remember why though.
18415         var el = this.ghost.dom.firstChild;
18416                 if(el){
18417                         Roo.fly(el).setStyle('float', 'none');
18418                 }
18419     },
18420     
18421     /**
18422      * Returns the underlying proxy {@link Roo.Layer}
18423      * @return {Roo.Layer} el
18424     */
18425     getEl : function(){
18426         return this.el;
18427     },
18428
18429     /**
18430      * Returns the ghost element
18431      * @return {Roo.Element} el
18432      */
18433     getGhost : function(){
18434         return this.ghost;
18435     },
18436
18437     /**
18438      * Hides the proxy
18439      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
18440      */
18441     hide : function(clear){
18442         this.el.hide();
18443         if(clear){
18444             this.reset(true);
18445         }
18446     },
18447
18448     /**
18449      * Stops the repair animation if it's currently running
18450      */
18451     stop : function(){
18452         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
18453             this.anim.stop();
18454         }
18455     },
18456
18457     /**
18458      * Displays this proxy
18459      */
18460     show : function(){
18461         this.el.show();
18462     },
18463
18464     /**
18465      * Force the Layer to sync its shadow and shim positions to the element
18466      */
18467     sync : function(){
18468         this.el.sync();
18469     },
18470
18471     /**
18472      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
18473      * invalid drop operation by the item being dragged.
18474      * @param {Array} xy The XY position of the element ([x, y])
18475      * @param {Function} callback The function to call after the repair is complete
18476      * @param {Object} scope The scope in which to execute the callback
18477      */
18478     repair : function(xy, callback, scope){
18479         this.callback = callback;
18480         this.scope = scope;
18481         if(xy && this.animRepair !== false){
18482             this.el.addClass("x-dd-drag-repair");
18483             this.el.hideUnders(true);
18484             this.anim = this.el.shift({
18485                 duration: this.repairDuration || .5,
18486                 easing: 'easeOut',
18487                 xy: xy,
18488                 stopFx: true,
18489                 callback: this.afterRepair,
18490                 scope: this
18491             });
18492         }else{
18493             this.afterRepair();
18494         }
18495     },
18496
18497     // private
18498     afterRepair : function(){
18499         this.hide(true);
18500         if(typeof this.callback == "function"){
18501             this.callback.call(this.scope || this);
18502         }
18503         this.callback = null;
18504         this.scope = null;
18505     }
18506 };/*
18507  * Based on:
18508  * Ext JS Library 1.1.1
18509  * Copyright(c) 2006-2007, Ext JS, LLC.
18510  *
18511  * Originally Released Under LGPL - original licence link has changed is not relivant.
18512  *
18513  * Fork - LGPL
18514  * <script type="text/javascript">
18515  */
18516
18517 /**
18518  * @class Roo.dd.DragSource
18519  * @extends Roo.dd.DDProxy
18520  * A simple class that provides the basic implementation needed to make any element draggable.
18521  * @constructor
18522  * @param {String/HTMLElement/Element} el The container element
18523  * @param {Object} config
18524  */
18525 Roo.dd.DragSource = function(el, config){
18526     this.el = Roo.get(el);
18527     this.dragData = {};
18528     
18529     Roo.apply(this, config);
18530     
18531     if(!this.proxy){
18532         this.proxy = new Roo.dd.StatusProxy();
18533     }
18534
18535     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18536           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18537     
18538     this.dragging = false;
18539 };
18540
18541 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18542     /**
18543      * @cfg {String} dropAllowed
18544      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18545      */
18546     dropAllowed : "x-dd-drop-ok",
18547     /**
18548      * @cfg {String} dropNotAllowed
18549      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18550      */
18551     dropNotAllowed : "x-dd-drop-nodrop",
18552
18553     /**
18554      * Returns the data object associated with this drag source
18555      * @return {Object} data An object containing arbitrary data
18556      */
18557     getDragData : function(e){
18558         return this.dragData;
18559     },
18560
18561     // private
18562     onDragEnter : function(e, id){
18563         var target = Roo.dd.DragDropMgr.getDDById(id);
18564         this.cachedTarget = target;
18565         if(this.beforeDragEnter(target, e, id) !== false){
18566             if(target.isNotifyTarget){
18567                 var status = target.notifyEnter(this, e, this.dragData);
18568                 this.proxy.setStatus(status);
18569             }else{
18570                 this.proxy.setStatus(this.dropAllowed);
18571             }
18572             
18573             if(this.afterDragEnter){
18574                 /**
18575                  * An empty function by default, but provided so that you can perform a custom action
18576                  * when the dragged item enters the drop target by providing an implementation.
18577                  * @param {Roo.dd.DragDrop} target The drop target
18578                  * @param {Event} e The event object
18579                  * @param {String} id The id of the dragged element
18580                  * @method afterDragEnter
18581                  */
18582                 this.afterDragEnter(target, e, id);
18583             }
18584         }
18585     },
18586
18587     /**
18588      * An empty function by default, but provided so that you can perform a custom action
18589      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18590      * @param {Roo.dd.DragDrop} target The drop target
18591      * @param {Event} e The event object
18592      * @param {String} id The id of the dragged element
18593      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18594      */
18595     beforeDragEnter : function(target, e, id){
18596         return true;
18597     },
18598
18599     // private
18600     alignElWithMouse: function() {
18601         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18602         this.proxy.sync();
18603     },
18604
18605     // private
18606     onDragOver : function(e, id){
18607         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18608         if(this.beforeDragOver(target, e, id) !== false){
18609             if(target.isNotifyTarget){
18610                 var status = target.notifyOver(this, e, this.dragData);
18611                 this.proxy.setStatus(status);
18612             }
18613
18614             if(this.afterDragOver){
18615                 /**
18616                  * An empty function by default, but provided so that you can perform a custom action
18617                  * while the dragged item is over the drop target by providing an implementation.
18618                  * @param {Roo.dd.DragDrop} target The drop target
18619                  * @param {Event} e The event object
18620                  * @param {String} id The id of the dragged element
18621                  * @method afterDragOver
18622                  */
18623                 this.afterDragOver(target, e, id);
18624             }
18625         }
18626     },
18627
18628     /**
18629      * An empty function by default, but provided so that you can perform a custom action
18630      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18631      * @param {Roo.dd.DragDrop} target The drop target
18632      * @param {Event} e The event object
18633      * @param {String} id The id of the dragged element
18634      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18635      */
18636     beforeDragOver : function(target, e, id){
18637         return true;
18638     },
18639
18640     // private
18641     onDragOut : function(e, id){
18642         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18643         if(this.beforeDragOut(target, e, id) !== false){
18644             if(target.isNotifyTarget){
18645                 target.notifyOut(this, e, this.dragData);
18646             }
18647             this.proxy.reset();
18648             if(this.afterDragOut){
18649                 /**
18650                  * An empty function by default, but provided so that you can perform a custom action
18651                  * after the dragged item is dragged out of the target without dropping.
18652                  * @param {Roo.dd.DragDrop} target The drop target
18653                  * @param {Event} e The event object
18654                  * @param {String} id The id of the dragged element
18655                  * @method afterDragOut
18656                  */
18657                 this.afterDragOut(target, e, id);
18658             }
18659         }
18660         this.cachedTarget = null;
18661     },
18662
18663     /**
18664      * An empty function by default, but provided so that you can perform a custom action before the dragged
18665      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18666      * @param {Roo.dd.DragDrop} target The drop target
18667      * @param {Event} e The event object
18668      * @param {String} id The id of the dragged element
18669      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18670      */
18671     beforeDragOut : function(target, e, id){
18672         return true;
18673     },
18674     
18675     // private
18676     onDragDrop : function(e, id){
18677         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18678         if(this.beforeDragDrop(target, e, id) !== false){
18679             if(target.isNotifyTarget){
18680                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18681                     this.onValidDrop(target, e, id);
18682                 }else{
18683                     this.onInvalidDrop(target, e, id);
18684                 }
18685             }else{
18686                 this.onValidDrop(target, e, id);
18687             }
18688             
18689             if(this.afterDragDrop){
18690                 /**
18691                  * An empty function by default, but provided so that you can perform a custom action
18692                  * after a valid drag drop has occurred by providing an implementation.
18693                  * @param {Roo.dd.DragDrop} target The drop target
18694                  * @param {Event} e The event object
18695                  * @param {String} id The id of the dropped element
18696                  * @method afterDragDrop
18697                  */
18698                 this.afterDragDrop(target, e, id);
18699             }
18700         }
18701         delete this.cachedTarget;
18702     },
18703
18704     /**
18705      * An empty function by default, but provided so that you can perform a custom action before the dragged
18706      * item is dropped onto the target and optionally cancel the onDragDrop.
18707      * @param {Roo.dd.DragDrop} target The drop target
18708      * @param {Event} e The event object
18709      * @param {String} id The id of the dragged element
18710      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18711      */
18712     beforeDragDrop : function(target, e, id){
18713         return true;
18714     },
18715
18716     // private
18717     onValidDrop : function(target, e, id){
18718         this.hideProxy();
18719         if(this.afterValidDrop){
18720             /**
18721              * An empty function by default, but provided so that you can perform a custom action
18722              * after a valid drop has occurred by providing an implementation.
18723              * @param {Object} target The target DD 
18724              * @param {Event} e The event object
18725              * @param {String} id The id of the dropped element
18726              * @method afterInvalidDrop
18727              */
18728             this.afterValidDrop(target, e, id);
18729         }
18730     },
18731
18732     // private
18733     getRepairXY : function(e, data){
18734         return this.el.getXY();  
18735     },
18736
18737     // private
18738     onInvalidDrop : function(target, e, id){
18739         this.beforeInvalidDrop(target, e, id);
18740         if(this.cachedTarget){
18741             if(this.cachedTarget.isNotifyTarget){
18742                 this.cachedTarget.notifyOut(this, e, this.dragData);
18743             }
18744             this.cacheTarget = null;
18745         }
18746         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18747
18748         if(this.afterInvalidDrop){
18749             /**
18750              * An empty function by default, but provided so that you can perform a custom action
18751              * after an invalid drop has occurred by providing an implementation.
18752              * @param {Event} e The event object
18753              * @param {String} id The id of the dropped element
18754              * @method afterInvalidDrop
18755              */
18756             this.afterInvalidDrop(e, id);
18757         }
18758     },
18759
18760     // private
18761     afterRepair : function(){
18762         if(Roo.enableFx){
18763             this.el.highlight(this.hlColor || "c3daf9");
18764         }
18765         this.dragging = false;
18766     },
18767
18768     /**
18769      * An empty function by default, but provided so that you can perform a custom action after an invalid
18770      * drop has occurred.
18771      * @param {Roo.dd.DragDrop} target The drop target
18772      * @param {Event} e The event object
18773      * @param {String} id The id of the dragged element
18774      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18775      */
18776     beforeInvalidDrop : function(target, e, id){
18777         return true;
18778     },
18779
18780     // private
18781     handleMouseDown : function(e){
18782         if(this.dragging) {
18783             return;
18784         }
18785         var data = this.getDragData(e);
18786         if(data && this.onBeforeDrag(data, e) !== false){
18787             this.dragData = data;
18788             this.proxy.stop();
18789             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18790         } 
18791     },
18792
18793     /**
18794      * An empty function by default, but provided so that you can perform a custom action before the initial
18795      * drag event begins and optionally cancel it.
18796      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18797      * @param {Event} e The event object
18798      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18799      */
18800     onBeforeDrag : function(data, e){
18801         return true;
18802     },
18803
18804     /**
18805      * An empty function by default, but provided so that you can perform a custom action once the initial
18806      * drag event has begun.  The drag cannot be canceled from this function.
18807      * @param {Number} x The x position of the click on the dragged object
18808      * @param {Number} y The y position of the click on the dragged object
18809      */
18810     onStartDrag : Roo.emptyFn,
18811
18812     // private - YUI override
18813     startDrag : function(x, y){
18814         this.proxy.reset();
18815         this.dragging = true;
18816         this.proxy.update("");
18817         this.onInitDrag(x, y);
18818         this.proxy.show();
18819     },
18820
18821     // private
18822     onInitDrag : function(x, y){
18823         var clone = this.el.dom.cloneNode(true);
18824         clone.id = Roo.id(); // prevent duplicate ids
18825         this.proxy.update(clone);
18826         this.onStartDrag(x, y);
18827         return true;
18828     },
18829
18830     /**
18831      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18832      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18833      */
18834     getProxy : function(){
18835         return this.proxy;  
18836     },
18837
18838     /**
18839      * Hides the drag source's {@link Roo.dd.StatusProxy}
18840      */
18841     hideProxy : function(){
18842         this.proxy.hide();  
18843         this.proxy.reset(true);
18844         this.dragging = false;
18845     },
18846
18847     // private
18848     triggerCacheRefresh : function(){
18849         Roo.dd.DDM.refreshCache(this.groups);
18850     },
18851
18852     // private - override to prevent hiding
18853     b4EndDrag: function(e) {
18854     },
18855
18856     // private - override to prevent moving
18857     endDrag : function(e){
18858         this.onEndDrag(this.dragData, e);
18859     },
18860
18861     // private
18862     onEndDrag : function(data, e){
18863     },
18864     
18865     // private - pin to cursor
18866     autoOffset : function(x, y) {
18867         this.setDelta(-12, -20);
18868     }    
18869 });/*
18870  * Based on:
18871  * Ext JS Library 1.1.1
18872  * Copyright(c) 2006-2007, Ext JS, LLC.
18873  *
18874  * Originally Released Under LGPL - original licence link has changed is not relivant.
18875  *
18876  * Fork - LGPL
18877  * <script type="text/javascript">
18878  */
18879
18880
18881 /**
18882  * @class Roo.dd.DropTarget
18883  * @extends Roo.dd.DDTarget
18884  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18885  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18886  * @constructor
18887  * @param {String/HTMLElement/Element} el The container element
18888  * @param {Object} config
18889  */
18890 Roo.dd.DropTarget = function(el, config){
18891     this.el = Roo.get(el);
18892     
18893     var listeners = false; ;
18894     if (config && config.listeners) {
18895         listeners= config.listeners;
18896         delete config.listeners;
18897     }
18898     Roo.apply(this, config);
18899     
18900     if(this.containerScroll){
18901         Roo.dd.ScrollManager.register(this.el);
18902     }
18903     this.addEvents( {
18904          /**
18905          * @scope Roo.dd.DropTarget
18906          */
18907          
18908          /**
18909          * @event enter
18910          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18911          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18912          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18913          * 
18914          * IMPORTANT : it should set this.overClass and this.dropAllowed
18915          * 
18916          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18917          * @param {Event} e The event
18918          * @param {Object} data An object containing arbitrary data supplied by the drag source
18919          */
18920         "enter" : true,
18921         
18922          /**
18923          * @event over
18924          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18925          * This method will be called on every mouse movement while the drag source is over the drop target.
18926          * This default implementation simply returns the dropAllowed config value.
18927          * 
18928          * IMPORTANT : it should set this.dropAllowed
18929          * 
18930          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18931          * @param {Event} e The event
18932          * @param {Object} data An object containing arbitrary data supplied by the drag source
18933          
18934          */
18935         "over" : true,
18936         /**
18937          * @event out
18938          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18939          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18940          * overClass (if any) from the drop element.
18941          * 
18942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18943          * @param {Event} e The event
18944          * @param {Object} data An object containing arbitrary data supplied by the drag source
18945          */
18946          "out" : true,
18947          
18948         /**
18949          * @event drop
18950          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18951          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18952          * implementation that does something to process the drop event and returns true so that the drag source's
18953          * repair action does not run.
18954          * 
18955          * IMPORTANT : it should set this.success
18956          * 
18957          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18958          * @param {Event} e The event
18959          * @param {Object} data An object containing arbitrary data supplied by the drag source
18960         */
18961          "drop" : true
18962     });
18963             
18964      
18965     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18966         this.el.dom, 
18967         this.ddGroup || this.group,
18968         {
18969             isTarget: true,
18970             listeners : listeners || {} 
18971            
18972         
18973         }
18974     );
18975
18976 };
18977
18978 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18979     /**
18980      * @cfg {String} overClass
18981      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18982      */
18983      /**
18984      * @cfg {String} ddGroup
18985      * The drag drop group to handle drop events for
18986      */
18987      
18988     /**
18989      * @cfg {String} dropAllowed
18990      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18991      */
18992     dropAllowed : "x-dd-drop-ok",
18993     /**
18994      * @cfg {String} dropNotAllowed
18995      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18996      */
18997     dropNotAllowed : "x-dd-drop-nodrop",
18998     /**
18999      * @cfg {boolean} success
19000      * set this after drop listener.. 
19001      */
19002     success : false,
19003     /**
19004      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
19005      * if the drop point is valid for over/enter..
19006      */
19007     valid : false,
19008     // private
19009     isTarget : true,
19010
19011     // private
19012     isNotifyTarget : true,
19013     
19014     /**
19015      * @hide
19016      */
19017     notifyEnter : function(dd, e, data)
19018     {
19019         this.valid = true;
19020         this.fireEvent('enter', dd, e, data);
19021         if(this.overClass){
19022             this.el.addClass(this.overClass);
19023         }
19024         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
19025             this.valid ? this.dropAllowed : this.dropNotAllowed
19026         );
19027     },
19028
19029     /**
19030      * @hide
19031      */
19032     notifyOver : function(dd, e, data)
19033     {
19034         this.valid = true;
19035         this.fireEvent('over', dd, e, data);
19036         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
19037             this.valid ? this.dropAllowed : this.dropNotAllowed
19038         );
19039     },
19040
19041     /**
19042      * @hide
19043      */
19044     notifyOut : function(dd, e, data)
19045     {
19046         this.fireEvent('out', dd, e, data);
19047         if(this.overClass){
19048             this.el.removeClass(this.overClass);
19049         }
19050     },
19051
19052     /**
19053      * @hide
19054      */
19055     notifyDrop : function(dd, e, data)
19056     {
19057         this.success = false;
19058         this.fireEvent('drop', dd, e, data);
19059         return this.success;
19060     }
19061 });/*
19062  * Based on:
19063  * Ext JS Library 1.1.1
19064  * Copyright(c) 2006-2007, Ext JS, LLC.
19065  *
19066  * Originally Released Under LGPL - original licence link has changed is not relivant.
19067  *
19068  * Fork - LGPL
19069  * <script type="text/javascript">
19070  */
19071
19072
19073 /**
19074  * @class Roo.dd.DragZone
19075  * @extends Roo.dd.DragSource
19076  * This class provides a container DD instance that proxies for multiple child node sources.<br />
19077  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
19078  * @constructor
19079  * @param {String/HTMLElement/Element} el The container element
19080  * @param {Object} config
19081  */
19082 Roo.dd.DragZone = function(el, config){
19083     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
19084     if(this.containerScroll){
19085         Roo.dd.ScrollManager.register(this.el);
19086     }
19087 };
19088
19089 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
19090     /**
19091      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
19092      * for auto scrolling during drag operations.
19093      */
19094     /**
19095      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
19096      * method after a failed drop (defaults to "c3daf9" - light blue)
19097      */
19098
19099     /**
19100      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
19101      * for a valid target to drag based on the mouse down. Override this method
19102      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
19103      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
19104      * @param {EventObject} e The mouse down event
19105      * @return {Object} The dragData
19106      */
19107     getDragData : function(e){
19108         return Roo.dd.Registry.getHandleFromEvent(e);
19109     },
19110     
19111     /**
19112      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
19113      * this.dragData.ddel
19114      * @param {Number} x The x position of the click on the dragged object
19115      * @param {Number} y The y position of the click on the dragged object
19116      * @return {Boolean} true to continue the drag, false to cancel
19117      */
19118     onInitDrag : function(x, y){
19119         this.proxy.update(this.dragData.ddel.cloneNode(true));
19120         this.onStartDrag(x, y);
19121         return true;
19122     },
19123     
19124     /**
19125      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
19126      */
19127     afterRepair : function(){
19128         if(Roo.enableFx){
19129             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
19130         }
19131         this.dragging = false;
19132     },
19133
19134     /**
19135      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
19136      * the XY of this.dragData.ddel
19137      * @param {EventObject} e The mouse up event
19138      * @return {Array} The xy location (e.g. [100, 200])
19139      */
19140     getRepairXY : function(e){
19141         return Roo.Element.fly(this.dragData.ddel).getXY();  
19142     }
19143 });/*
19144  * Based on:
19145  * Ext JS Library 1.1.1
19146  * Copyright(c) 2006-2007, Ext JS, LLC.
19147  *
19148  * Originally Released Under LGPL - original licence link has changed is not relivant.
19149  *
19150  * Fork - LGPL
19151  * <script type="text/javascript">
19152  */
19153 /**
19154  * @class Roo.dd.DropZone
19155  * @extends Roo.dd.DropTarget
19156  * This class provides a container DD instance that proxies for multiple child node targets.<br />
19157  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
19158  * @constructor
19159  * @param {String/HTMLElement/Element} el The container element
19160  * @param {Object} config
19161  */
19162 Roo.dd.DropZone = function(el, config){
19163     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
19164 };
19165
19166 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
19167     /**
19168      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
19169      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
19170      * provide your own custom lookup.
19171      * @param {Event} e The event
19172      * @return {Object} data The custom data
19173      */
19174     getTargetFromEvent : function(e){
19175         return Roo.dd.Registry.getTargetFromEvent(e);
19176     },
19177
19178     /**
19179      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
19180      * that it has registered.  This method has no default implementation and should be overridden to provide
19181      * node-specific processing if necessary.
19182      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
19183      * {@link #getTargetFromEvent} for this node)
19184      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19185      * @param {Event} e The event
19186      * @param {Object} data An object containing arbitrary data supplied by the drag source
19187      */
19188     onNodeEnter : function(n, dd, e, data){
19189         
19190     },
19191
19192     /**
19193      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
19194      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
19195      * overridden to provide the proper feedback.
19196      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19197      * {@link #getTargetFromEvent} for this node)
19198      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19199      * @param {Event} e The event
19200      * @param {Object} data An object containing arbitrary data supplied by the drag source
19201      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19202      * underlying {@link Roo.dd.StatusProxy} can be updated
19203      */
19204     onNodeOver : function(n, dd, e, data){
19205         return this.dropAllowed;
19206     },
19207
19208     /**
19209      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
19210      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
19211      * node-specific processing if necessary.
19212      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19213      * {@link #getTargetFromEvent} for this node)
19214      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19215      * @param {Event} e The event
19216      * @param {Object} data An object containing arbitrary data supplied by the drag source
19217      */
19218     onNodeOut : function(n, dd, e, data){
19219         
19220     },
19221
19222     /**
19223      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
19224      * the drop node.  The default implementation returns false, so it should be overridden to provide the
19225      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
19226      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19227      * {@link #getTargetFromEvent} for this node)
19228      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19229      * @param {Event} e The event
19230      * @param {Object} data An object containing arbitrary data supplied by the drag source
19231      * @return {Boolean} True if the drop was valid, else false
19232      */
19233     onNodeDrop : function(n, dd, e, data){
19234         return false;
19235     },
19236
19237     /**
19238      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
19239      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
19240      * it should be overridden to provide the proper feedback if necessary.
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     onContainerOver : function(dd, e, data){
19248         return this.dropNotAllowed;
19249     },
19250
19251     /**
19252      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
19253      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
19254      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
19255      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
19256      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19257      * @param {Event} e The event
19258      * @param {Object} data An object containing arbitrary data supplied by the drag source
19259      * @return {Boolean} True if the drop was valid, else false
19260      */
19261     onContainerDrop : function(dd, e, data){
19262         return false;
19263     },
19264
19265     /**
19266      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
19267      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
19268      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
19269      * you should override this method and provide a custom implementation.
19270      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19271      * @param {Event} e The event
19272      * @param {Object} data An object containing arbitrary data supplied by the drag source
19273      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19274      * underlying {@link Roo.dd.StatusProxy} can be updated
19275      */
19276     notifyEnter : function(dd, e, data){
19277         return this.dropNotAllowed;
19278     },
19279
19280     /**
19281      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
19282      * This method will be called on every mouse movement while the drag source is over the drop zone.
19283      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
19284      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
19285      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
19286      * registered node, it will call {@link #onContainerOver}.
19287      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19288      * @param {Event} e The event
19289      * @param {Object} data An object containing arbitrary data supplied by the drag source
19290      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19291      * underlying {@link Roo.dd.StatusProxy} can be updated
19292      */
19293     notifyOver : function(dd, e, data){
19294         var n = this.getTargetFromEvent(e);
19295         if(!n){ // not over valid drop target
19296             if(this.lastOverNode){
19297                 this.onNodeOut(this.lastOverNode, dd, e, data);
19298                 this.lastOverNode = null;
19299             }
19300             return this.onContainerOver(dd, e, data);
19301         }
19302         if(this.lastOverNode != n){
19303             if(this.lastOverNode){
19304                 this.onNodeOut(this.lastOverNode, dd, e, data);
19305             }
19306             this.onNodeEnter(n, dd, e, data);
19307             this.lastOverNode = n;
19308         }
19309         return this.onNodeOver(n, dd, e, data);
19310     },
19311
19312     /**
19313      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
19314      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
19315      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
19316      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
19317      * @param {Event} e The event
19318      * @param {Object} data An object containing arbitrary data supplied by the drag zone
19319      */
19320     notifyOut : function(dd, e, data){
19321         if(this.lastOverNode){
19322             this.onNodeOut(this.lastOverNode, dd, e, data);
19323             this.lastOverNode = null;
19324         }
19325     },
19326
19327     /**
19328      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
19329      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
19330      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
19331      * otherwise it will call {@link #onContainerDrop}.
19332      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19333      * @param {Event} e The event
19334      * @param {Object} data An object containing arbitrary data supplied by the drag source
19335      * @return {Boolean} True if the drop was valid, else false
19336      */
19337     notifyDrop : function(dd, e, data){
19338         if(this.lastOverNode){
19339             this.onNodeOut(this.lastOverNode, dd, e, data);
19340             this.lastOverNode = null;
19341         }
19342         var n = this.getTargetFromEvent(e);
19343         return n ?
19344             this.onNodeDrop(n, dd, e, data) :
19345             this.onContainerDrop(dd, e, data);
19346     },
19347
19348     // private
19349     triggerCacheRefresh : function(){
19350         Roo.dd.DDM.refreshCache(this.groups);
19351     }  
19352 });/*
19353  * Based on:
19354  * Ext JS Library 1.1.1
19355  * Copyright(c) 2006-2007, Ext JS, LLC.
19356  *
19357  * Originally Released Under LGPL - original licence link has changed is not relivant.
19358  *
19359  * Fork - LGPL
19360  * <script type="text/javascript">
19361  */
19362
19363
19364 /**
19365  * @class Roo.data.SortTypes
19366  * @singleton
19367  * Defines the default sorting (casting?) comparison functions used when sorting data.
19368  */
19369 Roo.data.SortTypes = {
19370     /**
19371      * Default sort that does nothing
19372      * @param {Mixed} s The value being converted
19373      * @return {Mixed} The comparison value
19374      */
19375     none : function(s){
19376         return s;
19377     },
19378     
19379     /**
19380      * The regular expression used to strip tags
19381      * @type {RegExp}
19382      * @property
19383      */
19384     stripTagsRE : /<\/?[^>]+>/gi,
19385     
19386     /**
19387      * Strips all HTML tags to sort on text only
19388      * @param {Mixed} s The value being converted
19389      * @return {String} The comparison value
19390      */
19391     asText : function(s){
19392         return String(s).replace(this.stripTagsRE, "");
19393     },
19394     
19395     /**
19396      * Strips all HTML tags to sort on text only - Case insensitive
19397      * @param {Mixed} s The value being converted
19398      * @return {String} The comparison value
19399      */
19400     asUCText : function(s){
19401         return String(s).toUpperCase().replace(this.stripTagsRE, "");
19402     },
19403     
19404     /**
19405      * Case insensitive string
19406      * @param {Mixed} s The value being converted
19407      * @return {String} The comparison value
19408      */
19409     asUCString : function(s) {
19410         return String(s).toUpperCase();
19411     },
19412     
19413     /**
19414      * Date sorting
19415      * @param {Mixed} s The value being converted
19416      * @return {Number} The comparison value
19417      */
19418     asDate : function(s) {
19419         if(!s){
19420             return 0;
19421         }
19422         if(s instanceof Date){
19423             return s.getTime();
19424         }
19425         return Date.parse(String(s));
19426     },
19427     
19428     /**
19429      * Float sorting
19430      * @param {Mixed} s The value being converted
19431      * @return {Float} The comparison value
19432      */
19433     asFloat : function(s) {
19434         var val = parseFloat(String(s).replace(/,/g, ""));
19435         if(isNaN(val)) val = 0;
19436         return val;
19437     },
19438     
19439     /**
19440      * Integer sorting
19441      * @param {Mixed} s The value being converted
19442      * @return {Number} The comparison value
19443      */
19444     asInt : function(s) {
19445         var val = parseInt(String(s).replace(/,/g, ""));
19446         if(isNaN(val)) val = 0;
19447         return val;
19448     }
19449 };/*
19450  * Based on:
19451  * Ext JS Library 1.1.1
19452  * Copyright(c) 2006-2007, Ext JS, LLC.
19453  *
19454  * Originally Released Under LGPL - original licence link has changed is not relivant.
19455  *
19456  * Fork - LGPL
19457  * <script type="text/javascript">
19458  */
19459
19460 /**
19461 * @class Roo.data.Record
19462  * Instances of this class encapsulate both record <em>definition</em> information, and record
19463  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
19464  * to access Records cached in an {@link Roo.data.Store} object.<br>
19465  * <p>
19466  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
19467  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
19468  * objects.<br>
19469  * <p>
19470  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
19471  * @constructor
19472  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
19473  * {@link #create}. The parameters are the same.
19474  * @param {Array} data An associative Array of data values keyed by the field name.
19475  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
19476  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
19477  * not specified an integer id is generated.
19478  */
19479 Roo.data.Record = function(data, id){
19480     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
19481     this.data = data;
19482 };
19483
19484 /**
19485  * Generate a constructor for a specific record layout.
19486  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
19487  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
19488  * Each field definition object may contain the following properties: <ul>
19489  * <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,
19490  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
19491  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
19492  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
19493  * is being used, then this is a string containing the javascript expression to reference the data relative to 
19494  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
19495  * to the data item relative to the record element. If the mapping expression is the same as the field name,
19496  * this may be omitted.</p></li>
19497  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
19498  * <ul><li>auto (Default, implies no conversion)</li>
19499  * <li>string</li>
19500  * <li>int</li>
19501  * <li>float</li>
19502  * <li>boolean</li>
19503  * <li>date</li></ul></p></li>
19504  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
19505  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19506  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19507  * by the Reader into an object that will be stored in the Record. It is passed the
19508  * following parameters:<ul>
19509  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19510  * </ul></p></li>
19511  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19512  * </ul>
19513  * <br>usage:<br><pre><code>
19514 var TopicRecord = Roo.data.Record.create(
19515     {name: 'title', mapping: 'topic_title'},
19516     {name: 'author', mapping: 'username'},
19517     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19518     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19519     {name: 'lastPoster', mapping: 'user2'},
19520     {name: 'excerpt', mapping: 'post_text'}
19521 );
19522
19523 var myNewRecord = new TopicRecord({
19524     title: 'Do my job please',
19525     author: 'noobie',
19526     totalPosts: 1,
19527     lastPost: new Date(),
19528     lastPoster: 'Animal',
19529     excerpt: 'No way dude!'
19530 });
19531 myStore.add(myNewRecord);
19532 </code></pre>
19533  * @method create
19534  * @static
19535  */
19536 Roo.data.Record.create = function(o){
19537     var f = function(){
19538         f.superclass.constructor.apply(this, arguments);
19539     };
19540     Roo.extend(f, Roo.data.Record);
19541     var p = f.prototype;
19542     p.fields = new Roo.util.MixedCollection(false, function(field){
19543         return field.name;
19544     });
19545     for(var i = 0, len = o.length; i < len; i++){
19546         p.fields.add(new Roo.data.Field(o[i]));
19547     }
19548     f.getField = function(name){
19549         return p.fields.get(name);  
19550     };
19551     return f;
19552 };
19553
19554 Roo.data.Record.AUTO_ID = 1000;
19555 Roo.data.Record.EDIT = 'edit';
19556 Roo.data.Record.REJECT = 'reject';
19557 Roo.data.Record.COMMIT = 'commit';
19558
19559 Roo.data.Record.prototype = {
19560     /**
19561      * Readonly flag - true if this record has been modified.
19562      * @type Boolean
19563      */
19564     dirty : false,
19565     editing : false,
19566     error: null,
19567     modified: null,
19568
19569     // private
19570     join : function(store){
19571         this.store = store;
19572     },
19573
19574     /**
19575      * Set the named field to the specified value.
19576      * @param {String} name The name of the field to set.
19577      * @param {Object} value The value to set the field to.
19578      */
19579     set : function(name, value){
19580         if(this.data[name] == value){
19581             return;
19582         }
19583         this.dirty = true;
19584         if(!this.modified){
19585             this.modified = {};
19586         }
19587         if(typeof this.modified[name] == 'undefined'){
19588             this.modified[name] = this.data[name];
19589         }
19590         this.data[name] = value;
19591         if(!this.editing && this.store){
19592             this.store.afterEdit(this);
19593         }       
19594     },
19595
19596     /**
19597      * Get the value of the named field.
19598      * @param {String} name The name of the field to get the value of.
19599      * @return {Object} The value of the field.
19600      */
19601     get : function(name){
19602         return this.data[name]; 
19603     },
19604
19605     // private
19606     beginEdit : function(){
19607         this.editing = true;
19608         this.modified = {}; 
19609     },
19610
19611     // private
19612     cancelEdit : function(){
19613         this.editing = false;
19614         delete this.modified;
19615     },
19616
19617     // private
19618     endEdit : function(){
19619         this.editing = false;
19620         if(this.dirty && this.store){
19621             this.store.afterEdit(this);
19622         }
19623     },
19624
19625     /**
19626      * Usually called by the {@link Roo.data.Store} which owns the Record.
19627      * Rejects all changes made to the Record since either creation, or the last commit operation.
19628      * Modified fields are reverted to their original values.
19629      * <p>
19630      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19631      * of reject operations.
19632      */
19633     reject : function(){
19634         var m = this.modified;
19635         for(var n in m){
19636             if(typeof m[n] != "function"){
19637                 this.data[n] = m[n];
19638             }
19639         }
19640         this.dirty = false;
19641         delete this.modified;
19642         this.editing = false;
19643         if(this.store){
19644             this.store.afterReject(this);
19645         }
19646     },
19647
19648     /**
19649      * Usually called by the {@link Roo.data.Store} which owns the Record.
19650      * Commits all changes made to the Record since either creation, or the last commit operation.
19651      * <p>
19652      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19653      * of commit operations.
19654      */
19655     commit : function(){
19656         this.dirty = false;
19657         delete this.modified;
19658         this.editing = false;
19659         if(this.store){
19660             this.store.afterCommit(this);
19661         }
19662     },
19663
19664     // private
19665     hasError : function(){
19666         return this.error != null;
19667     },
19668
19669     // private
19670     clearError : function(){
19671         this.error = null;
19672     },
19673
19674     /**
19675      * Creates a copy of this record.
19676      * @param {String} id (optional) A new record id if you don't want to use this record's id
19677      * @return {Record}
19678      */
19679     copy : function(newId) {
19680         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19681     }
19682 };/*
19683  * Based on:
19684  * Ext JS Library 1.1.1
19685  * Copyright(c) 2006-2007, Ext JS, LLC.
19686  *
19687  * Originally Released Under LGPL - original licence link has changed is not relivant.
19688  *
19689  * Fork - LGPL
19690  * <script type="text/javascript">
19691  */
19692
19693
19694
19695 /**
19696  * @class Roo.data.Store
19697  * @extends Roo.util.Observable
19698  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19699  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19700  * <p>
19701  * 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
19702  * has no knowledge of the format of the data returned by the Proxy.<br>
19703  * <p>
19704  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19705  * instances from the data object. These records are cached and made available through accessor functions.
19706  * @constructor
19707  * Creates a new Store.
19708  * @param {Object} config A config object containing the objects needed for the Store to access data,
19709  * and read the data into Records.
19710  */
19711 Roo.data.Store = function(config){
19712     this.data = new Roo.util.MixedCollection(false);
19713     this.data.getKey = function(o){
19714         return o.id;
19715     };
19716     this.baseParams = {};
19717     // private
19718     this.paramNames = {
19719         "start" : "start",
19720         "limit" : "limit",
19721         "sort" : "sort",
19722         "dir" : "dir",
19723         "multisort" : "_multisort"
19724     };
19725
19726     if(config && config.data){
19727         this.inlineData = config.data;
19728         delete config.data;
19729     }
19730
19731     Roo.apply(this, config);
19732     
19733     if(this.reader){ // reader passed
19734         this.reader = Roo.factory(this.reader, Roo.data);
19735         this.reader.xmodule = this.xmodule || false;
19736         if(!this.recordType){
19737             this.recordType = this.reader.recordType;
19738         }
19739         if(this.reader.onMetaChange){
19740             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19741         }
19742     }
19743
19744     if(this.recordType){
19745         this.fields = this.recordType.prototype.fields;
19746     }
19747     this.modified = [];
19748
19749     this.addEvents({
19750         /**
19751          * @event datachanged
19752          * Fires when the data cache has changed, and a widget which is using this Store
19753          * as a Record cache should refresh its view.
19754          * @param {Store} this
19755          */
19756         datachanged : true,
19757         /**
19758          * @event metachange
19759          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19760          * @param {Store} this
19761          * @param {Object} meta The JSON metadata
19762          */
19763         metachange : true,
19764         /**
19765          * @event add
19766          * Fires when Records have been added to the Store
19767          * @param {Store} this
19768          * @param {Roo.data.Record[]} records The array of Records added
19769          * @param {Number} index The index at which the record(s) were added
19770          */
19771         add : true,
19772         /**
19773          * @event remove
19774          * Fires when a Record has been removed from the Store
19775          * @param {Store} this
19776          * @param {Roo.data.Record} record The Record that was removed
19777          * @param {Number} index The index at which the record was removed
19778          */
19779         remove : true,
19780         /**
19781          * @event update
19782          * Fires when a Record has been updated
19783          * @param {Store} this
19784          * @param {Roo.data.Record} record The Record that was updated
19785          * @param {String} operation The update operation being performed.  Value may be one of:
19786          * <pre><code>
19787  Roo.data.Record.EDIT
19788  Roo.data.Record.REJECT
19789  Roo.data.Record.COMMIT
19790          * </code></pre>
19791          */
19792         update : true,
19793         /**
19794          * @event clear
19795          * Fires when the data cache has been cleared.
19796          * @param {Store} this
19797          */
19798         clear : true,
19799         /**
19800          * @event beforeload
19801          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19802          * the load action will be canceled.
19803          * @param {Store} this
19804          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19805          */
19806         beforeload : true,
19807         /**
19808          * @event load
19809          * Fires after a new set of Records has been loaded.
19810          * @param {Store} this
19811          * @param {Roo.data.Record[]} records The Records that were loaded
19812          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19813          */
19814         load : true,
19815         /**
19816          * @event loadexception
19817          * Fires if an exception occurs in the Proxy during loading.
19818          * Called with the signature of the Proxy's "loadexception" event.
19819          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19820          * 
19821          * @param {Proxy} 
19822          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19823          * @param {Object} load options 
19824          * @param {Object} jsonData from your request (normally this contains the Exception)
19825          */
19826         loadexception : true
19827     });
19828     
19829     if(this.proxy){
19830         this.proxy = Roo.factory(this.proxy, Roo.data);
19831         this.proxy.xmodule = this.xmodule || false;
19832         this.relayEvents(this.proxy,  ["loadexception"]);
19833     }
19834     this.sortToggle = {};
19835     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19836
19837     Roo.data.Store.superclass.constructor.call(this);
19838
19839     if(this.inlineData){
19840         this.loadData(this.inlineData);
19841         delete this.inlineData;
19842     }
19843 };
19844 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19845      /**
19846     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19847     * without a remote query - used by combo/forms at present.
19848     */
19849     
19850     /**
19851     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19852     */
19853     /**
19854     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19855     */
19856     /**
19857     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19858     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19859     */
19860     /**
19861     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19862     * on any HTTP request
19863     */
19864     /**
19865     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19866     */
19867     /**
19868     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19869     */
19870     multiSort: false,
19871     /**
19872     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19873     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19874     */
19875     remoteSort : false,
19876
19877     /**
19878     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19879      * loaded or when a record is removed. (defaults to false).
19880     */
19881     pruneModifiedRecords : false,
19882
19883     // private
19884     lastOptions : null,
19885
19886     /**
19887      * Add Records to the Store and fires the add event.
19888      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19889      */
19890     add : function(records){
19891         records = [].concat(records);
19892         for(var i = 0, len = records.length; i < len; i++){
19893             records[i].join(this);
19894         }
19895         var index = this.data.length;
19896         this.data.addAll(records);
19897         this.fireEvent("add", this, records, index);
19898     },
19899
19900     /**
19901      * Remove a Record from the Store and fires the remove event.
19902      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19903      */
19904     remove : function(record){
19905         var index = this.data.indexOf(record);
19906         this.data.removeAt(index);
19907         if(this.pruneModifiedRecords){
19908             this.modified.remove(record);
19909         }
19910         this.fireEvent("remove", this, record, index);
19911     },
19912
19913     /**
19914      * Remove all Records from the Store and fires the clear event.
19915      */
19916     removeAll : function(){
19917         this.data.clear();
19918         if(this.pruneModifiedRecords){
19919             this.modified = [];
19920         }
19921         this.fireEvent("clear", this);
19922     },
19923
19924     /**
19925      * Inserts Records to the Store at the given index and fires the add event.
19926      * @param {Number} index The start index at which to insert the passed Records.
19927      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19928      */
19929     insert : function(index, records){
19930         records = [].concat(records);
19931         for(var i = 0, len = records.length; i < len; i++){
19932             this.data.insert(index, records[i]);
19933             records[i].join(this);
19934         }
19935         this.fireEvent("add", this, records, index);
19936     },
19937
19938     /**
19939      * Get the index within the cache of the passed Record.
19940      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19941      * @return {Number} The index of the passed Record. Returns -1 if not found.
19942      */
19943     indexOf : function(record){
19944         return this.data.indexOf(record);
19945     },
19946
19947     /**
19948      * Get the index within the cache of the Record with the passed id.
19949      * @param {String} id The id of the Record to find.
19950      * @return {Number} The index of the Record. Returns -1 if not found.
19951      */
19952     indexOfId : function(id){
19953         return this.data.indexOfKey(id);
19954     },
19955
19956     /**
19957      * Get the Record with the specified id.
19958      * @param {String} id The id of the Record to find.
19959      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19960      */
19961     getById : function(id){
19962         return this.data.key(id);
19963     },
19964
19965     /**
19966      * Get the Record at the specified index.
19967      * @param {Number} index The index of the Record to find.
19968      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19969      */
19970     getAt : function(index){
19971         return this.data.itemAt(index);
19972     },
19973
19974     /**
19975      * Returns a range of Records between specified indices.
19976      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19977      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19978      * @return {Roo.data.Record[]} An array of Records
19979      */
19980     getRange : function(start, end){
19981         return this.data.getRange(start, end);
19982     },
19983
19984     // private
19985     storeOptions : function(o){
19986         o = Roo.apply({}, o);
19987         delete o.callback;
19988         delete o.scope;
19989         this.lastOptions = o;
19990     },
19991
19992     /**
19993      * Loads the Record cache from the configured Proxy using the configured Reader.
19994      * <p>
19995      * If using remote paging, then the first load call must specify the <em>start</em>
19996      * and <em>limit</em> properties in the options.params property to establish the initial
19997      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19998      * <p>
19999      * <strong>It is important to note that for remote data sources, loading is asynchronous,
20000      * and this call will return before the new data has been loaded. Perform any post-processing
20001      * in a callback function, or in a "load" event handler.</strong>
20002      * <p>
20003      * @param {Object} options An object containing properties which control loading options:<ul>
20004      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
20005      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
20006      * passed the following arguments:<ul>
20007      * <li>r : Roo.data.Record[]</li>
20008      * <li>options: Options object from the load call</li>
20009      * <li>success: Boolean success indicator</li></ul></li>
20010      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
20011      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
20012      * </ul>
20013      */
20014     load : function(options){
20015         options = options || {};
20016         if(this.fireEvent("beforeload", this, options) !== false){
20017             this.storeOptions(options);
20018             var p = Roo.apply(options.params || {}, this.baseParams);
20019             // if meta was not loaded from remote source.. try requesting it.
20020             if (!this.reader.metaFromRemote) {
20021                 p._requestMeta = 1;
20022             }
20023             if(this.sortInfo && this.remoteSort){
20024                 var pn = this.paramNames;
20025                 p[pn["sort"]] = this.sortInfo.field;
20026                 p[pn["dir"]] = this.sortInfo.direction;
20027             }
20028             if (this.multiSort) {
20029                 var pn = this.paramNames;
20030                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
20031             }
20032             
20033             this.proxy.load(p, this.reader, this.loadRecords, this, options);
20034         }
20035     },
20036
20037     /**
20038      * Reloads the Record cache from the configured Proxy using the configured Reader and
20039      * the options from the last load operation performed.
20040      * @param {Object} options (optional) An object containing properties which may override the options
20041      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
20042      * the most recently used options are reused).
20043      */
20044     reload : function(options){
20045         this.load(Roo.applyIf(options||{}, this.lastOptions));
20046     },
20047
20048     // private
20049     // Called as a callback by the Reader during a load operation.
20050     loadRecords : function(o, options, success){
20051         if(!o || success === false){
20052             if(success !== false){
20053                 this.fireEvent("load", this, [], options);
20054             }
20055             if(options.callback){
20056                 options.callback.call(options.scope || this, [], options, false);
20057             }
20058             return;
20059         }
20060         // if data returned failure - throw an exception.
20061         if (o.success === false) {
20062             // show a message if no listener is registered.
20063             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
20064                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
20065             }
20066             // loadmask wil be hooked into this..
20067             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
20068             return;
20069         }
20070         var r = o.records, t = o.totalRecords || r.length;
20071         if(!options || options.add !== true){
20072             if(this.pruneModifiedRecords){
20073                 this.modified = [];
20074             }
20075             for(var i = 0, len = r.length; i < len; i++){
20076                 r[i].join(this);
20077             }
20078             if(this.snapshot){
20079                 this.data = this.snapshot;
20080                 delete this.snapshot;
20081             }
20082             this.data.clear();
20083             this.data.addAll(r);
20084             this.totalLength = t;
20085             this.applySort();
20086             this.fireEvent("datachanged", this);
20087         }else{
20088             this.totalLength = Math.max(t, this.data.length+r.length);
20089             this.add(r);
20090         }
20091         this.fireEvent("load", this, r, options);
20092         if(options.callback){
20093             options.callback.call(options.scope || this, r, options, true);
20094         }
20095     },
20096
20097
20098     /**
20099      * Loads data from a passed data block. A Reader which understands the format of the data
20100      * must have been configured in the constructor.
20101      * @param {Object} data The data block from which to read the Records.  The format of the data expected
20102      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
20103      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
20104      */
20105     loadData : function(o, append){
20106         var r = this.reader.readRecords(o);
20107         this.loadRecords(r, {add: append}, true);
20108     },
20109
20110     /**
20111      * Gets the number of cached records.
20112      * <p>
20113      * <em>If using paging, this may not be the total size of the dataset. If the data object
20114      * used by the Reader contains the dataset size, then the getTotalCount() function returns
20115      * the data set size</em>
20116      */
20117     getCount : function(){
20118         return this.data.length || 0;
20119     },
20120
20121     /**
20122      * Gets the total number of records in the dataset as returned by the server.
20123      * <p>
20124      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
20125      * the dataset size</em>
20126      */
20127     getTotalCount : function(){
20128         return this.totalLength || 0;
20129     },
20130
20131     /**
20132      * Returns the sort state of the Store as an object with two properties:
20133      * <pre><code>
20134  field {String} The name of the field by which the Records are sorted
20135  direction {String} The sort order, "ASC" or "DESC"
20136      * </code></pre>
20137      */
20138     getSortState : function(){
20139         return this.sortInfo;
20140     },
20141
20142     // private
20143     applySort : function(){
20144         if(this.sortInfo && !this.remoteSort){
20145             var s = this.sortInfo, f = s.field;
20146             var st = this.fields.get(f).sortType;
20147             var fn = function(r1, r2){
20148                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
20149                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
20150             };
20151             this.data.sort(s.direction, fn);
20152             if(this.snapshot && this.snapshot != this.data){
20153                 this.snapshot.sort(s.direction, fn);
20154             }
20155         }
20156     },
20157
20158     /**
20159      * Sets the default sort column and order to be used by the next load operation.
20160      * @param {String} fieldName The name of the field to sort by.
20161      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20162      */
20163     setDefaultSort : function(field, dir){
20164         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
20165     },
20166
20167     /**
20168      * Sort the Records.
20169      * If remote sorting is used, the sort is performed on the server, and the cache is
20170      * reloaded. If local sorting is used, the cache is sorted internally.
20171      * @param {String} fieldName The name of the field to sort by.
20172      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20173      */
20174     sort : function(fieldName, dir){
20175         var f = this.fields.get(fieldName);
20176         if(!dir){
20177             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
20178             
20179             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
20180                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
20181             }else{
20182                 dir = f.sortDir;
20183             }
20184         }
20185         this.sortToggle[f.name] = dir;
20186         this.sortInfo = {field: f.name, direction: dir};
20187         if(!this.remoteSort){
20188             this.applySort();
20189             this.fireEvent("datachanged", this);
20190         }else{
20191             this.load(this.lastOptions);
20192         }
20193     },
20194
20195     /**
20196      * Calls the specified function for each of the Records in the cache.
20197      * @param {Function} fn The function to call. The Record is passed as the first parameter.
20198      * Returning <em>false</em> aborts and exits the iteration.
20199      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
20200      */
20201     each : function(fn, scope){
20202         this.data.each(fn, scope);
20203     },
20204
20205     /**
20206      * Gets all records modified since the last commit.  Modified records are persisted across load operations
20207      * (e.g., during paging).
20208      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
20209      */
20210     getModifiedRecords : function(){
20211         return this.modified;
20212     },
20213
20214     // private
20215     createFilterFn : function(property, value, anyMatch){
20216         if(!value.exec){ // not a regex
20217             value = String(value);
20218             if(value.length == 0){
20219                 return false;
20220             }
20221             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
20222         }
20223         return function(r){
20224             return value.test(r.data[property]);
20225         };
20226     },
20227
20228     /**
20229      * Sums the value of <i>property</i> for each record between start and end and returns the result.
20230      * @param {String} property A field on your records
20231      * @param {Number} start The record index to start at (defaults to 0)
20232      * @param {Number} end The last record index to include (defaults to length - 1)
20233      * @return {Number} The sum
20234      */
20235     sum : function(property, start, end){
20236         var rs = this.data.items, v = 0;
20237         start = start || 0;
20238         end = (end || end === 0) ? end : rs.length-1;
20239
20240         for(var i = start; i <= end; i++){
20241             v += (rs[i].data[property] || 0);
20242         }
20243         return v;
20244     },
20245
20246     /**
20247      * Filter the records by a specified property.
20248      * @param {String} field A field on your records
20249      * @param {String/RegExp} value Either a string that the field
20250      * should start with or a RegExp to test against the field
20251      * @param {Boolean} anyMatch True to match any part not just the beginning
20252      */
20253     filter : function(property, value, anyMatch){
20254         var fn = this.createFilterFn(property, value, anyMatch);
20255         return fn ? this.filterBy(fn) : this.clearFilter();
20256     },
20257
20258     /**
20259      * Filter by a function. The specified function will be called with each
20260      * record in this data source. If the function returns true the record is included,
20261      * otherwise it is filtered.
20262      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20263      * @param {Object} scope (optional) The scope of the function (defaults to this)
20264      */
20265     filterBy : function(fn, scope){
20266         this.snapshot = this.snapshot || this.data;
20267         this.data = this.queryBy(fn, scope||this);
20268         this.fireEvent("datachanged", this);
20269     },
20270
20271     /**
20272      * Query the records by a specified property.
20273      * @param {String} field A field on your records
20274      * @param {String/RegExp} value Either a string that the field
20275      * should start with or a RegExp to test against the field
20276      * @param {Boolean} anyMatch True to match any part not just the beginning
20277      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20278      */
20279     query : function(property, value, anyMatch){
20280         var fn = this.createFilterFn(property, value, anyMatch);
20281         return fn ? this.queryBy(fn) : this.data.clone();
20282     },
20283
20284     /**
20285      * Query by a function. The specified function will be called with each
20286      * record in this data source. If the function returns true the record is included
20287      * in the results.
20288      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20289      * @param {Object} scope (optional) The scope of the function (defaults to this)
20290       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20291      **/
20292     queryBy : function(fn, scope){
20293         var data = this.snapshot || this.data;
20294         return data.filterBy(fn, scope||this);
20295     },
20296
20297     /**
20298      * Collects unique values for a particular dataIndex from this store.
20299      * @param {String} dataIndex The property to collect
20300      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
20301      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
20302      * @return {Array} An array of the unique values
20303      **/
20304     collect : function(dataIndex, allowNull, bypassFilter){
20305         var d = (bypassFilter === true && this.snapshot) ?
20306                 this.snapshot.items : this.data.items;
20307         var v, sv, r = [], l = {};
20308         for(var i = 0, len = d.length; i < len; i++){
20309             v = d[i].data[dataIndex];
20310             sv = String(v);
20311             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
20312                 l[sv] = true;
20313                 r[r.length] = v;
20314             }
20315         }
20316         return r;
20317     },
20318
20319     /**
20320      * Revert to a view of the Record cache with no filtering applied.
20321      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
20322      */
20323     clearFilter : function(suppressEvent){
20324         if(this.snapshot && this.snapshot != this.data){
20325             this.data = this.snapshot;
20326             delete this.snapshot;
20327             if(suppressEvent !== true){
20328                 this.fireEvent("datachanged", this);
20329             }
20330         }
20331     },
20332
20333     // private
20334     afterEdit : function(record){
20335         if(this.modified.indexOf(record) == -1){
20336             this.modified.push(record);
20337         }
20338         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
20339     },
20340     
20341     // private
20342     afterReject : function(record){
20343         this.modified.remove(record);
20344         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
20345     },
20346
20347     // private
20348     afterCommit : function(record){
20349         this.modified.remove(record);
20350         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
20351     },
20352
20353     /**
20354      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
20355      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
20356      */
20357     commitChanges : function(){
20358         var m = this.modified.slice(0);
20359         this.modified = [];
20360         for(var i = 0, len = m.length; i < len; i++){
20361             m[i].commit();
20362         }
20363     },
20364
20365     /**
20366      * Cancel outstanding changes on all changed records.
20367      */
20368     rejectChanges : function(){
20369         var m = this.modified.slice(0);
20370         this.modified = [];
20371         for(var i = 0, len = m.length; i < len; i++){
20372             m[i].reject();
20373         }
20374     },
20375
20376     onMetaChange : function(meta, rtype, o){
20377         this.recordType = rtype;
20378         this.fields = rtype.prototype.fields;
20379         delete this.snapshot;
20380         this.sortInfo = meta.sortInfo || this.sortInfo;
20381         this.modified = [];
20382         this.fireEvent('metachange', this, this.reader.meta);
20383     }
20384 });/*
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  * @class Roo.data.SimpleStore
20397  * @extends Roo.data.Store
20398  * Small helper class to make creating Stores from Array data easier.
20399  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
20400  * @cfg {Array} fields An array of field definition objects, or field name strings.
20401  * @cfg {Array} data The multi-dimensional array of data
20402  * @constructor
20403  * @param {Object} config
20404  */
20405 Roo.data.SimpleStore = function(config){
20406     Roo.data.SimpleStore.superclass.constructor.call(this, {
20407         isLocal : true,
20408         reader: new Roo.data.ArrayReader({
20409                 id: config.id
20410             },
20411             Roo.data.Record.create(config.fields)
20412         ),
20413         proxy : new Roo.data.MemoryProxy(config.data)
20414     });
20415     this.load();
20416 };
20417 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
20418  * Based on:
20419  * Ext JS Library 1.1.1
20420  * Copyright(c) 2006-2007, Ext JS, LLC.
20421  *
20422  * Originally Released Under LGPL - original licence link has changed is not relivant.
20423  *
20424  * Fork - LGPL
20425  * <script type="text/javascript">
20426  */
20427
20428 /**
20429 /**
20430  * @extends Roo.data.Store
20431  * @class Roo.data.JsonStore
20432  * Small helper class to make creating Stores for JSON data easier. <br/>
20433 <pre><code>
20434 var store = new Roo.data.JsonStore({
20435     url: 'get-images.php',
20436     root: 'images',
20437     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
20438 });
20439 </code></pre>
20440  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
20441  * JsonReader and HttpProxy (unless inline data is provided).</b>
20442  * @cfg {Array} fields An array of field definition objects, or field name strings.
20443  * @constructor
20444  * @param {Object} config
20445  */
20446 Roo.data.JsonStore = function(c){
20447     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
20448         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
20449         reader: new Roo.data.JsonReader(c, c.fields)
20450     }));
20451 };
20452 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
20453  * Based on:
20454  * Ext JS Library 1.1.1
20455  * Copyright(c) 2006-2007, Ext JS, LLC.
20456  *
20457  * Originally Released Under LGPL - original licence link has changed is not relivant.
20458  *
20459  * Fork - LGPL
20460  * <script type="text/javascript">
20461  */
20462
20463  
20464 Roo.data.Field = function(config){
20465     if(typeof config == "string"){
20466         config = {name: config};
20467     }
20468     Roo.apply(this, config);
20469     
20470     if(!this.type){
20471         this.type = "auto";
20472     }
20473     
20474     var st = Roo.data.SortTypes;
20475     // named sortTypes are supported, here we look them up
20476     if(typeof this.sortType == "string"){
20477         this.sortType = st[this.sortType];
20478     }
20479     
20480     // set default sortType for strings and dates
20481     if(!this.sortType){
20482         switch(this.type){
20483             case "string":
20484                 this.sortType = st.asUCString;
20485                 break;
20486             case "date":
20487                 this.sortType = st.asDate;
20488                 break;
20489             default:
20490                 this.sortType = st.none;
20491         }
20492     }
20493
20494     // define once
20495     var stripRe = /[\$,%]/g;
20496
20497     // prebuilt conversion function for this field, instead of
20498     // switching every time we're reading a value
20499     if(!this.convert){
20500         var cv, dateFormat = this.dateFormat;
20501         switch(this.type){
20502             case "":
20503             case "auto":
20504             case undefined:
20505                 cv = function(v){ return v; };
20506                 break;
20507             case "string":
20508                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20509                 break;
20510             case "int":
20511                 cv = function(v){
20512                     return v !== undefined && v !== null && v !== '' ?
20513                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20514                     };
20515                 break;
20516             case "float":
20517                 cv = function(v){
20518                     return v !== undefined && v !== null && v !== '' ?
20519                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20520                     };
20521                 break;
20522             case "bool":
20523             case "boolean":
20524                 cv = function(v){ return v === true || v === "true" || v == 1; };
20525                 break;
20526             case "date":
20527                 cv = function(v){
20528                     if(!v){
20529                         return '';
20530                     }
20531                     if(v instanceof Date){
20532                         return v;
20533                     }
20534                     if(dateFormat){
20535                         if(dateFormat == "timestamp"){
20536                             return new Date(v*1000);
20537                         }
20538                         return Date.parseDate(v, dateFormat);
20539                     }
20540                     var parsed = Date.parse(v);
20541                     return parsed ? new Date(parsed) : null;
20542                 };
20543              break;
20544             
20545         }
20546         this.convert = cv;
20547     }
20548 };
20549
20550 Roo.data.Field.prototype = {
20551     dateFormat: null,
20552     defaultValue: "",
20553     mapping: null,
20554     sortType : null,
20555     sortDir : "ASC"
20556 };/*
20557  * Based on:
20558  * Ext JS Library 1.1.1
20559  * Copyright(c) 2006-2007, Ext JS, LLC.
20560  *
20561  * Originally Released Under LGPL - original licence link has changed is not relivant.
20562  *
20563  * Fork - LGPL
20564  * <script type="text/javascript">
20565  */
20566  
20567 // Base class for reading structured data from a data source.  This class is intended to be
20568 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20569
20570 /**
20571  * @class Roo.data.DataReader
20572  * Base class for reading structured data from a data source.  This class is intended to be
20573  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20574  */
20575
20576 Roo.data.DataReader = function(meta, recordType){
20577     
20578     this.meta = meta;
20579     
20580     this.recordType = recordType instanceof Array ? 
20581         Roo.data.Record.create(recordType) : recordType;
20582 };
20583
20584 Roo.data.DataReader.prototype = {
20585      /**
20586      * Create an empty record
20587      * @param {Object} data (optional) - overlay some values
20588      * @return {Roo.data.Record} record created.
20589      */
20590     newRow :  function(d) {
20591         var da =  {};
20592         this.recordType.prototype.fields.each(function(c) {
20593             switch( c.type) {
20594                 case 'int' : da[c.name] = 0; break;
20595                 case 'date' : da[c.name] = new Date(); break;
20596                 case 'float' : da[c.name] = 0.0; break;
20597                 case 'boolean' : da[c.name] = false; break;
20598                 default : da[c.name] = ""; break;
20599             }
20600             
20601         });
20602         return new this.recordType(Roo.apply(da, d));
20603     }
20604     
20605 };/*
20606  * Based on:
20607  * Ext JS Library 1.1.1
20608  * Copyright(c) 2006-2007, Ext JS, LLC.
20609  *
20610  * Originally Released Under LGPL - original licence link has changed is not relivant.
20611  *
20612  * Fork - LGPL
20613  * <script type="text/javascript">
20614  */
20615
20616 /**
20617  * @class Roo.data.DataProxy
20618  * @extends Roo.data.Observable
20619  * This class is an abstract base class for implementations which provide retrieval of
20620  * unformatted data objects.<br>
20621  * <p>
20622  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20623  * (of the appropriate type which knows how to parse the data object) to provide a block of
20624  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20625  * <p>
20626  * Custom implementations must implement the load method as described in
20627  * {@link Roo.data.HttpProxy#load}.
20628  */
20629 Roo.data.DataProxy = function(){
20630     this.addEvents({
20631         /**
20632          * @event beforeload
20633          * Fires before a network request is made to retrieve a data object.
20634          * @param {Object} This DataProxy object.
20635          * @param {Object} params The params parameter to the load function.
20636          */
20637         beforeload : true,
20638         /**
20639          * @event load
20640          * Fires before the load method's callback is called.
20641          * @param {Object} This DataProxy object.
20642          * @param {Object} o The data object.
20643          * @param {Object} arg The callback argument object passed to the load function.
20644          */
20645         load : true,
20646         /**
20647          * @event loadexception
20648          * Fires if an Exception occurs during data retrieval.
20649          * @param {Object} This DataProxy object.
20650          * @param {Object} o The data object.
20651          * @param {Object} arg The callback argument object passed to the load function.
20652          * @param {Object} e The Exception.
20653          */
20654         loadexception : true
20655     });
20656     Roo.data.DataProxy.superclass.constructor.call(this);
20657 };
20658
20659 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20660
20661     /**
20662      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20663      */
20664 /*
20665  * Based on:
20666  * Ext JS Library 1.1.1
20667  * Copyright(c) 2006-2007, Ext JS, LLC.
20668  *
20669  * Originally Released Under LGPL - original licence link has changed is not relivant.
20670  *
20671  * Fork - LGPL
20672  * <script type="text/javascript">
20673  */
20674 /**
20675  * @class Roo.data.MemoryProxy
20676  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20677  * to the Reader when its load method is called.
20678  * @constructor
20679  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20680  */
20681 Roo.data.MemoryProxy = function(data){
20682     if (data.data) {
20683         data = data.data;
20684     }
20685     Roo.data.MemoryProxy.superclass.constructor.call(this);
20686     this.data = data;
20687 };
20688
20689 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20690     /**
20691      * Load data from the requested source (in this case an in-memory
20692      * data object passed to the constructor), read the data object into
20693      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20694      * process that block using the passed callback.
20695      * @param {Object} params This parameter is not used by the MemoryProxy class.
20696      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20697      * object into a block of Roo.data.Records.
20698      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20699      * The function must be passed <ul>
20700      * <li>The Record block object</li>
20701      * <li>The "arg" argument from the load function</li>
20702      * <li>A boolean success indicator</li>
20703      * </ul>
20704      * @param {Object} scope The scope in which to call the callback
20705      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20706      */
20707     load : function(params, reader, callback, scope, arg){
20708         params = params || {};
20709         var result;
20710         try {
20711             result = reader.readRecords(this.data);
20712         }catch(e){
20713             this.fireEvent("loadexception", this, arg, null, e);
20714             callback.call(scope, null, arg, false);
20715             return;
20716         }
20717         callback.call(scope, result, arg, true);
20718     },
20719     
20720     // private
20721     update : function(params, records){
20722         
20723     }
20724 });/*
20725  * Based on:
20726  * Ext JS Library 1.1.1
20727  * Copyright(c) 2006-2007, Ext JS, LLC.
20728  *
20729  * Originally Released Under LGPL - original licence link has changed is not relivant.
20730  *
20731  * Fork - LGPL
20732  * <script type="text/javascript">
20733  */
20734 /**
20735  * @class Roo.data.HttpProxy
20736  * @extends Roo.data.DataProxy
20737  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20738  * configured to reference a certain URL.<br><br>
20739  * <p>
20740  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20741  * from which the running page was served.<br><br>
20742  * <p>
20743  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20744  * <p>
20745  * Be aware that to enable the browser to parse an XML document, the server must set
20746  * the Content-Type header in the HTTP response to "text/xml".
20747  * @constructor
20748  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20749  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20750  * will be used to make the request.
20751  */
20752 Roo.data.HttpProxy = function(conn){
20753     Roo.data.HttpProxy.superclass.constructor.call(this);
20754     // is conn a conn config or a real conn?
20755     this.conn = conn;
20756     this.useAjax = !conn || !conn.events;
20757   
20758 };
20759
20760 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20761     // thse are take from connection...
20762     
20763     /**
20764      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20765      */
20766     /**
20767      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20768      * extra parameters to each request made by this object. (defaults to undefined)
20769      */
20770     /**
20771      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20772      *  to each request made by this object. (defaults to undefined)
20773      */
20774     /**
20775      * @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)
20776      */
20777     /**
20778      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20779      */
20780      /**
20781      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20782      * @type Boolean
20783      */
20784   
20785
20786     /**
20787      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20788      * @type Boolean
20789      */
20790     /**
20791      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20792      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20793      * a finer-grained basis than the DataProxy events.
20794      */
20795     getConnection : function(){
20796         return this.useAjax ? Roo.Ajax : this.conn;
20797     },
20798
20799     /**
20800      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20801      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20802      * process that block using the passed callback.
20803      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20804      * for the request to the remote server.
20805      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20806      * object into a block of Roo.data.Records.
20807      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20808      * The function must be passed <ul>
20809      * <li>The Record block object</li>
20810      * <li>The "arg" argument from the load function</li>
20811      * <li>A boolean success indicator</li>
20812      * </ul>
20813      * @param {Object} scope The scope in which to call the callback
20814      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20815      */
20816     load : function(params, reader, callback, scope, arg){
20817         if(this.fireEvent("beforeload", this, params) !== false){
20818             var  o = {
20819                 params : params || {},
20820                 request: {
20821                     callback : callback,
20822                     scope : scope,
20823                     arg : arg
20824                 },
20825                 reader: reader,
20826                 callback : this.loadResponse,
20827                 scope: this
20828             };
20829             if(this.useAjax){
20830                 Roo.applyIf(o, this.conn);
20831                 if(this.activeRequest){
20832                     Roo.Ajax.abort(this.activeRequest);
20833                 }
20834                 this.activeRequest = Roo.Ajax.request(o);
20835             }else{
20836                 this.conn.request(o);
20837             }
20838         }else{
20839             callback.call(scope||this, null, arg, false);
20840         }
20841     },
20842
20843     // private
20844     loadResponse : function(o, success, response){
20845         delete this.activeRequest;
20846         if(!success){
20847             this.fireEvent("loadexception", this, o, response);
20848             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20849             return;
20850         }
20851         var result;
20852         try {
20853             result = o.reader.read(response);
20854         }catch(e){
20855             this.fireEvent("loadexception", this, o, response, e);
20856             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20857             return;
20858         }
20859         
20860         this.fireEvent("load", this, o, o.request.arg);
20861         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20862     },
20863
20864     // private
20865     update : function(dataSet){
20866
20867     },
20868
20869     // private
20870     updateResponse : function(dataSet){
20871
20872     }
20873 });/*
20874  * Based on:
20875  * Ext JS Library 1.1.1
20876  * Copyright(c) 2006-2007, Ext JS, LLC.
20877  *
20878  * Originally Released Under LGPL - original licence link has changed is not relivant.
20879  *
20880  * Fork - LGPL
20881  * <script type="text/javascript">
20882  */
20883
20884 /**
20885  * @class Roo.data.ScriptTagProxy
20886  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20887  * other than the originating domain of the running page.<br><br>
20888  * <p>
20889  * <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
20890  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20891  * <p>
20892  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20893  * source code that is used as the source inside a &lt;script> tag.<br><br>
20894  * <p>
20895  * In order for the browser to process the returned data, the server must wrap the data object
20896  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20897  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20898  * depending on whether the callback name was passed:
20899  * <p>
20900  * <pre><code>
20901 boolean scriptTag = false;
20902 String cb = request.getParameter("callback");
20903 if (cb != null) {
20904     scriptTag = true;
20905     response.setContentType("text/javascript");
20906 } else {
20907     response.setContentType("application/x-json");
20908 }
20909 Writer out = response.getWriter();
20910 if (scriptTag) {
20911     out.write(cb + "(");
20912 }
20913 out.print(dataBlock.toJsonString());
20914 if (scriptTag) {
20915     out.write(");");
20916 }
20917 </pre></code>
20918  *
20919  * @constructor
20920  * @param {Object} config A configuration object.
20921  */
20922 Roo.data.ScriptTagProxy = function(config){
20923     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20924     Roo.apply(this, config);
20925     this.head = document.getElementsByTagName("head")[0];
20926 };
20927
20928 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20929
20930 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20931     /**
20932      * @cfg {String} url The URL from which to request the data object.
20933      */
20934     /**
20935      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20936      */
20937     timeout : 30000,
20938     /**
20939      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20940      * the server the name of the callback function set up by the load call to process the returned data object.
20941      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20942      * javascript output which calls this named function passing the data object as its only parameter.
20943      */
20944     callbackParam : "callback",
20945     /**
20946      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20947      * name to the request.
20948      */
20949     nocache : true,
20950
20951     /**
20952      * Load data from the configured URL, read the data object into
20953      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20954      * process that block using the passed callback.
20955      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20956      * for the request to the remote server.
20957      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20958      * object into a block of Roo.data.Records.
20959      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20960      * The function must be passed <ul>
20961      * <li>The Record block object</li>
20962      * <li>The "arg" argument from the load function</li>
20963      * <li>A boolean success indicator</li>
20964      * </ul>
20965      * @param {Object} scope The scope in which to call the callback
20966      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20967      */
20968     load : function(params, reader, callback, scope, arg){
20969         if(this.fireEvent("beforeload", this, params) !== false){
20970
20971             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20972
20973             var url = this.url;
20974             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20975             if(this.nocache){
20976                 url += "&_dc=" + (new Date().getTime());
20977             }
20978             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20979             var trans = {
20980                 id : transId,
20981                 cb : "stcCallback"+transId,
20982                 scriptId : "stcScript"+transId,
20983                 params : params,
20984                 arg : arg,
20985                 url : url,
20986                 callback : callback,
20987                 scope : scope,
20988                 reader : reader
20989             };
20990             var conn = this;
20991
20992             window[trans.cb] = function(o){
20993                 conn.handleResponse(o, trans);
20994             };
20995
20996             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20997
20998             if(this.autoAbort !== false){
20999                 this.abort();
21000             }
21001
21002             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
21003
21004             var script = document.createElement("script");
21005             script.setAttribute("src", url);
21006             script.setAttribute("type", "text/javascript");
21007             script.setAttribute("id", trans.scriptId);
21008             this.head.appendChild(script);
21009
21010             this.trans = trans;
21011         }else{
21012             callback.call(scope||this, null, arg, false);
21013         }
21014     },
21015
21016     // private
21017     isLoading : function(){
21018         return this.trans ? true : false;
21019     },
21020
21021     /**
21022      * Abort the current server request.
21023      */
21024     abort : function(){
21025         if(this.isLoading()){
21026             this.destroyTrans(this.trans);
21027         }
21028     },
21029
21030     // private
21031     destroyTrans : function(trans, isLoaded){
21032         this.head.removeChild(document.getElementById(trans.scriptId));
21033         clearTimeout(trans.timeoutId);
21034         if(isLoaded){
21035             window[trans.cb] = undefined;
21036             try{
21037                 delete window[trans.cb];
21038             }catch(e){}
21039         }else{
21040             // if hasn't been loaded, wait for load to remove it to prevent script error
21041             window[trans.cb] = function(){
21042                 window[trans.cb] = undefined;
21043                 try{
21044                     delete window[trans.cb];
21045                 }catch(e){}
21046             };
21047         }
21048     },
21049
21050     // private
21051     handleResponse : function(o, trans){
21052         this.trans = false;
21053         this.destroyTrans(trans, true);
21054         var result;
21055         try {
21056             result = trans.reader.readRecords(o);
21057         }catch(e){
21058             this.fireEvent("loadexception", this, o, trans.arg, e);
21059             trans.callback.call(trans.scope||window, null, trans.arg, false);
21060             return;
21061         }
21062         this.fireEvent("load", this, o, trans.arg);
21063         trans.callback.call(trans.scope||window, result, trans.arg, true);
21064     },
21065
21066     // private
21067     handleFailure : function(trans){
21068         this.trans = false;
21069         this.destroyTrans(trans, false);
21070         this.fireEvent("loadexception", this, null, trans.arg);
21071         trans.callback.call(trans.scope||window, null, trans.arg, false);
21072     }
21073 });/*
21074  * Based on:
21075  * Ext JS Library 1.1.1
21076  * Copyright(c) 2006-2007, Ext JS, LLC.
21077  *
21078  * Originally Released Under LGPL - original licence link has changed is not relivant.
21079  *
21080  * Fork - LGPL
21081  * <script type="text/javascript">
21082  */
21083
21084 /**
21085  * @class Roo.data.JsonReader
21086  * @extends Roo.data.DataReader
21087  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
21088  * based on mappings in a provided Roo.data.Record constructor.
21089  * 
21090  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
21091  * in the reply previously. 
21092  * 
21093  * <p>
21094  * Example code:
21095  * <pre><code>
21096 var RecordDef = Roo.data.Record.create([
21097     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21098     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21099 ]);
21100 var myReader = new Roo.data.JsonReader({
21101     totalProperty: "results",    // The property which contains the total dataset size (optional)
21102     root: "rows",                // The property which contains an Array of row objects
21103     id: "id"                     // The property within each row object that provides an ID for the record (optional)
21104 }, RecordDef);
21105 </code></pre>
21106  * <p>
21107  * This would consume a JSON file like this:
21108  * <pre><code>
21109 { 'results': 2, 'rows': [
21110     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
21111     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
21112 }
21113 </code></pre>
21114  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
21115  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21116  * paged from the remote server.
21117  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
21118  * @cfg {String} root name of the property which contains the Array of row objects.
21119  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
21120  * @constructor
21121  * Create a new JsonReader
21122  * @param {Object} meta Metadata configuration options
21123  * @param {Object} recordType Either an Array of field definition objects,
21124  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
21125  */
21126 Roo.data.JsonReader = function(meta, recordType){
21127     
21128     meta = meta || {};
21129     // set some defaults:
21130     Roo.applyIf(meta, {
21131         totalProperty: 'total',
21132         successProperty : 'success',
21133         root : 'data',
21134         id : 'id'
21135     });
21136     
21137     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21138 };
21139 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
21140     
21141     /**
21142      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
21143      * Used by Store query builder to append _requestMeta to params.
21144      * 
21145      */
21146     metaFromRemote : false,
21147     /**
21148      * This method is only used by a DataProxy which has retrieved data from a remote server.
21149      * @param {Object} response The XHR object which contains the JSON data in its responseText.
21150      * @return {Object} data A data block which is used by an Roo.data.Store object as
21151      * a cache of Roo.data.Records.
21152      */
21153     read : function(response){
21154         var json = response.responseText;
21155        
21156         var o = /* eval:var:o */ eval("("+json+")");
21157         if(!o) {
21158             throw {message: "JsonReader.read: Json object not found"};
21159         }
21160         
21161         if(o.metaData){
21162             
21163             delete this.ef;
21164             this.metaFromRemote = true;
21165             this.meta = o.metaData;
21166             this.recordType = Roo.data.Record.create(o.metaData.fields);
21167             this.onMetaChange(this.meta, this.recordType, o);
21168         }
21169         return this.readRecords(o);
21170     },
21171
21172     // private function a store will implement
21173     onMetaChange : function(meta, recordType, o){
21174
21175     },
21176
21177     /**
21178          * @ignore
21179          */
21180     simpleAccess: function(obj, subsc) {
21181         return obj[subsc];
21182     },
21183
21184         /**
21185          * @ignore
21186          */
21187     getJsonAccessor: function(){
21188         var re = /[\[\.]/;
21189         return function(expr) {
21190             try {
21191                 return(re.test(expr))
21192                     ? new Function("obj", "return obj." + expr)
21193                     : function(obj){
21194                         return obj[expr];
21195                     };
21196             } catch(e){}
21197             return Roo.emptyFn;
21198         };
21199     }(),
21200
21201     /**
21202      * Create a data block containing Roo.data.Records from an XML document.
21203      * @param {Object} o An object which contains an Array of row objects in the property specified
21204      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
21205      * which contains the total size of the dataset.
21206      * @return {Object} data A data block which is used by an Roo.data.Store object as
21207      * a cache of Roo.data.Records.
21208      */
21209     readRecords : function(o){
21210         /**
21211          * After any data loads, the raw JSON data is available for further custom processing.
21212          * @type Object
21213          */
21214         this.jsonData = o;
21215         var s = this.meta, Record = this.recordType,
21216             f = Record.prototype.fields, fi = f.items, fl = f.length;
21217
21218 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
21219         if (!this.ef) {
21220             if(s.totalProperty) {
21221                     this.getTotal = this.getJsonAccessor(s.totalProperty);
21222                 }
21223                 if(s.successProperty) {
21224                     this.getSuccess = this.getJsonAccessor(s.successProperty);
21225                 }
21226                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
21227                 if (s.id) {
21228                         var g = this.getJsonAccessor(s.id);
21229                         this.getId = function(rec) {
21230                                 var r = g(rec);
21231                                 return (r === undefined || r === "") ? null : r;
21232                         };
21233                 } else {
21234                         this.getId = function(){return null;};
21235                 }
21236             this.ef = [];
21237             for(var jj = 0; jj < fl; jj++){
21238                 f = fi[jj];
21239                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
21240                 this.ef[jj] = this.getJsonAccessor(map);
21241             }
21242         }
21243
21244         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
21245         if(s.totalProperty){
21246             var vt = parseInt(this.getTotal(o), 10);
21247             if(!isNaN(vt)){
21248                 totalRecords = vt;
21249             }
21250         }
21251         if(s.successProperty){
21252             var vs = this.getSuccess(o);
21253             if(vs === false || vs === 'false'){
21254                 success = false;
21255             }
21256         }
21257         var records = [];
21258             for(var i = 0; i < c; i++){
21259                     var n = root[i];
21260                 var values = {};
21261                 var id = this.getId(n);
21262                 for(var j = 0; j < fl; j++){
21263                     f = fi[j];
21264                 var v = this.ef[j](n);
21265                 if (!f.convert) {
21266                     Roo.log('missing convert for ' + f.name);
21267                     Roo.log(f);
21268                     continue;
21269                 }
21270                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
21271                 }
21272                 var record = new Record(values, id);
21273                 record.json = n;
21274                 records[i] = record;
21275             }
21276             return {
21277                 success : success,
21278                 records : records,
21279                 totalRecords : totalRecords
21280             };
21281     }
21282 });/*
21283  * Based on:
21284  * Ext JS Library 1.1.1
21285  * Copyright(c) 2006-2007, Ext JS, LLC.
21286  *
21287  * Originally Released Under LGPL - original licence link has changed is not relivant.
21288  *
21289  * Fork - LGPL
21290  * <script type="text/javascript">
21291  */
21292
21293 /**
21294  * @class Roo.data.XmlReader
21295  * @extends Roo.data.DataReader
21296  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
21297  * based on mappings in a provided Roo.data.Record constructor.<br><br>
21298  * <p>
21299  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
21300  * header in the HTTP response must be set to "text/xml".</em>
21301  * <p>
21302  * Example code:
21303  * <pre><code>
21304 var RecordDef = Roo.data.Record.create([
21305    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21306    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21307 ]);
21308 var myReader = new Roo.data.XmlReader({
21309    totalRecords: "results", // The element which contains the total dataset size (optional)
21310    record: "row",           // The repeated element which contains row information
21311    id: "id"                 // The element within the row that provides an ID for the record (optional)
21312 }, RecordDef);
21313 </code></pre>
21314  * <p>
21315  * This would consume an XML file like this:
21316  * <pre><code>
21317 &lt;?xml?>
21318 &lt;dataset>
21319  &lt;results>2&lt;/results>
21320  &lt;row>
21321    &lt;id>1&lt;/id>
21322    &lt;name>Bill&lt;/name>
21323    &lt;occupation>Gardener&lt;/occupation>
21324  &lt;/row>
21325  &lt;row>
21326    &lt;id>2&lt;/id>
21327    &lt;name>Ben&lt;/name>
21328    &lt;occupation>Horticulturalist&lt;/occupation>
21329  &lt;/row>
21330 &lt;/dataset>
21331 </code></pre>
21332  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
21333  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21334  * paged from the remote server.
21335  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
21336  * @cfg {String} success The DomQuery path to the success attribute used by forms.
21337  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
21338  * a record identifier value.
21339  * @constructor
21340  * Create a new XmlReader
21341  * @param {Object} meta Metadata configuration options
21342  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
21343  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
21344  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
21345  */
21346 Roo.data.XmlReader = function(meta, recordType){
21347     meta = meta || {};
21348     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21349 };
21350 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
21351     /**
21352      * This method is only used by a DataProxy which has retrieved data from a remote server.
21353          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
21354          * to contain a method called 'responseXML' that returns an XML document object.
21355      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21356      * a cache of Roo.data.Records.
21357      */
21358     read : function(response){
21359         var doc = response.responseXML;
21360         if(!doc) {
21361             throw {message: "XmlReader.read: XML Document not available"};
21362         }
21363         return this.readRecords(doc);
21364     },
21365
21366     /**
21367      * Create a data block containing Roo.data.Records from an XML document.
21368          * @param {Object} doc A parsed XML document.
21369      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21370      * a cache of Roo.data.Records.
21371      */
21372     readRecords : function(doc){
21373         /**
21374          * After any data loads/reads, the raw XML Document is available for further custom processing.
21375          * @type XMLDocument
21376          */
21377         this.xmlData = doc;
21378         var root = doc.documentElement || doc;
21379         var q = Roo.DomQuery;
21380         var recordType = this.recordType, fields = recordType.prototype.fields;
21381         var sid = this.meta.id;
21382         var totalRecords = 0, success = true;
21383         if(this.meta.totalRecords){
21384             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
21385         }
21386         
21387         if(this.meta.success){
21388             var sv = q.selectValue(this.meta.success, root, true);
21389             success = sv !== false && sv !== 'false';
21390         }
21391         var records = [];
21392         var ns = q.select(this.meta.record, root);
21393         for(var i = 0, len = ns.length; i < len; i++) {
21394                 var n = ns[i];
21395                 var values = {};
21396                 var id = sid ? q.selectValue(sid, n) : undefined;
21397                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21398                     var f = fields.items[j];
21399                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
21400                     v = f.convert(v);
21401                     values[f.name] = v;
21402                 }
21403                 var record = new recordType(values, id);
21404                 record.node = n;
21405                 records[records.length] = record;
21406             }
21407
21408             return {
21409                 success : success,
21410                 records : records,
21411                 totalRecords : totalRecords || records.length
21412             };
21413     }
21414 });/*
21415  * Based on:
21416  * Ext JS Library 1.1.1
21417  * Copyright(c) 2006-2007, Ext JS, LLC.
21418  *
21419  * Originally Released Under LGPL - original licence link has changed is not relivant.
21420  *
21421  * Fork - LGPL
21422  * <script type="text/javascript">
21423  */
21424
21425 /**
21426  * @class Roo.data.ArrayReader
21427  * @extends Roo.data.DataReader
21428  * Data reader class to create an Array of Roo.data.Record objects from an Array.
21429  * Each element of that Array represents a row of data fields. The
21430  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
21431  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
21432  * <p>
21433  * Example code:.
21434  * <pre><code>
21435 var RecordDef = Roo.data.Record.create([
21436     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
21437     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
21438 ]);
21439 var myReader = new Roo.data.ArrayReader({
21440     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
21441 }, RecordDef);
21442 </code></pre>
21443  * <p>
21444  * This would consume an Array like this:
21445  * <pre><code>
21446 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
21447   </code></pre>
21448  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
21449  * @constructor
21450  * Create a new JsonReader
21451  * @param {Object} meta Metadata configuration options.
21452  * @param {Object} recordType Either an Array of field definition objects
21453  * as specified to {@link Roo.data.Record#create},
21454  * or an {@link Roo.data.Record} object
21455  * created using {@link Roo.data.Record#create}.
21456  */
21457 Roo.data.ArrayReader = function(meta, recordType){
21458     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
21459 };
21460
21461 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
21462     /**
21463      * Create a data block containing Roo.data.Records from an XML document.
21464      * @param {Object} o An Array of row objects which represents the dataset.
21465      * @return {Object} data A data block which is used by an Roo.data.Store object as
21466      * a cache of Roo.data.Records.
21467      */
21468     readRecords : function(o){
21469         var sid = this.meta ? this.meta.id : null;
21470         var recordType = this.recordType, fields = recordType.prototype.fields;
21471         var records = [];
21472         var root = o;
21473             for(var i = 0; i < root.length; i++){
21474                     var n = root[i];
21475                 var values = {};
21476                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
21477                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21478                 var f = fields.items[j];
21479                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
21480                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
21481                 v = f.convert(v);
21482                 values[f.name] = v;
21483             }
21484                 var record = new recordType(values, id);
21485                 record.json = n;
21486                 records[records.length] = record;
21487             }
21488             return {
21489                 records : records,
21490                 totalRecords : records.length
21491             };
21492     }
21493 });/*
21494  * Based on:
21495  * Ext JS Library 1.1.1
21496  * Copyright(c) 2006-2007, Ext JS, LLC.
21497  *
21498  * Originally Released Under LGPL - original licence link has changed is not relivant.
21499  *
21500  * Fork - LGPL
21501  * <script type="text/javascript">
21502  */
21503
21504
21505 /**
21506  * @class Roo.data.Tree
21507  * @extends Roo.util.Observable
21508  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21509  * in the tree have most standard DOM functionality.
21510  * @constructor
21511  * @param {Node} root (optional) The root node
21512  */
21513 Roo.data.Tree = function(root){
21514    this.nodeHash = {};
21515    /**
21516     * The root node for this tree
21517     * @type Node
21518     */
21519    this.root = null;
21520    if(root){
21521        this.setRootNode(root);
21522    }
21523    this.addEvents({
21524        /**
21525         * @event append
21526         * Fires when a new child node is appended to a node in this tree.
21527         * @param {Tree} tree The owner tree
21528         * @param {Node} parent The parent node
21529         * @param {Node} node The newly appended node
21530         * @param {Number} index The index of the newly appended node
21531         */
21532        "append" : true,
21533        /**
21534         * @event remove
21535         * Fires when a child node is removed from a node in this tree.
21536         * @param {Tree} tree The owner tree
21537         * @param {Node} parent The parent node
21538         * @param {Node} node The child node removed
21539         */
21540        "remove" : true,
21541        /**
21542         * @event move
21543         * Fires when a node is moved to a new location in the tree
21544         * @param {Tree} tree The owner tree
21545         * @param {Node} node The node moved
21546         * @param {Node} oldParent The old parent of this node
21547         * @param {Node} newParent The new parent of this node
21548         * @param {Number} index The index it was moved to
21549         */
21550        "move" : true,
21551        /**
21552         * @event insert
21553         * Fires when a new child node is inserted in a node in this tree.
21554         * @param {Tree} tree The owner tree
21555         * @param {Node} parent The parent node
21556         * @param {Node} node The child node inserted
21557         * @param {Node} refNode The child node the node was inserted before
21558         */
21559        "insert" : true,
21560        /**
21561         * @event beforeappend
21562         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21563         * @param {Tree} tree The owner tree
21564         * @param {Node} parent The parent node
21565         * @param {Node} node The child node to be appended
21566         */
21567        "beforeappend" : true,
21568        /**
21569         * @event beforeremove
21570         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21571         * @param {Tree} tree The owner tree
21572         * @param {Node} parent The parent node
21573         * @param {Node} node The child node to be removed
21574         */
21575        "beforeremove" : true,
21576        /**
21577         * @event beforemove
21578         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21579         * @param {Tree} tree The owner tree
21580         * @param {Node} node The node being moved
21581         * @param {Node} oldParent The parent of the node
21582         * @param {Node} newParent The new parent the node is moving to
21583         * @param {Number} index The index it is being moved to
21584         */
21585        "beforemove" : true,
21586        /**
21587         * @event beforeinsert
21588         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21589         * @param {Tree} tree The owner tree
21590         * @param {Node} parent The parent node
21591         * @param {Node} node The child node to be inserted
21592         * @param {Node} refNode The child node the node is being inserted before
21593         */
21594        "beforeinsert" : true
21595    });
21596
21597     Roo.data.Tree.superclass.constructor.call(this);
21598 };
21599
21600 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21601     pathSeparator: "/",
21602
21603     proxyNodeEvent : function(){
21604         return this.fireEvent.apply(this, arguments);
21605     },
21606
21607     /**
21608      * Returns the root node for this tree.
21609      * @return {Node}
21610      */
21611     getRootNode : function(){
21612         return this.root;
21613     },
21614
21615     /**
21616      * Sets the root node for this tree.
21617      * @param {Node} node
21618      * @return {Node}
21619      */
21620     setRootNode : function(node){
21621         this.root = node;
21622         node.ownerTree = this;
21623         node.isRoot = true;
21624         this.registerNode(node);
21625         return node;
21626     },
21627
21628     /**
21629      * Gets a node in this tree by its id.
21630      * @param {String} id
21631      * @return {Node}
21632      */
21633     getNodeById : function(id){
21634         return this.nodeHash[id];
21635     },
21636
21637     registerNode : function(node){
21638         this.nodeHash[node.id] = node;
21639     },
21640
21641     unregisterNode : function(node){
21642         delete this.nodeHash[node.id];
21643     },
21644
21645     toString : function(){
21646         return "[Tree"+(this.id?" "+this.id:"")+"]";
21647     }
21648 });
21649
21650 /**
21651  * @class Roo.data.Node
21652  * @extends Roo.util.Observable
21653  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21654  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21655  * @constructor
21656  * @param {Object} attributes The attributes/config for the node
21657  */
21658 Roo.data.Node = function(attributes){
21659     /**
21660      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21661      * @type {Object}
21662      */
21663     this.attributes = attributes || {};
21664     this.leaf = this.attributes.leaf;
21665     /**
21666      * The node id. @type String
21667      */
21668     this.id = this.attributes.id;
21669     if(!this.id){
21670         this.id = Roo.id(null, "ynode-");
21671         this.attributes.id = this.id;
21672     }
21673      
21674     
21675     /**
21676      * All child nodes of this node. @type Array
21677      */
21678     this.childNodes = [];
21679     if(!this.childNodes.indexOf){ // indexOf is a must
21680         this.childNodes.indexOf = function(o){
21681             for(var i = 0, len = this.length; i < len; i++){
21682                 if(this[i] == o) {
21683                     return i;
21684                 }
21685             }
21686             return -1;
21687         };
21688     }
21689     /**
21690      * The parent node for this node. @type Node
21691      */
21692     this.parentNode = null;
21693     /**
21694      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21695      */
21696     this.firstChild = null;
21697     /**
21698      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21699      */
21700     this.lastChild = null;
21701     /**
21702      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21703      */
21704     this.previousSibling = null;
21705     /**
21706      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21707      */
21708     this.nextSibling = null;
21709
21710     this.addEvents({
21711        /**
21712         * @event append
21713         * Fires when a new child node is appended
21714         * @param {Tree} tree The owner tree
21715         * @param {Node} this This node
21716         * @param {Node} node The newly appended node
21717         * @param {Number} index The index of the newly appended node
21718         */
21719        "append" : true,
21720        /**
21721         * @event remove
21722         * Fires when a child node is removed
21723         * @param {Tree} tree The owner tree
21724         * @param {Node} this This node
21725         * @param {Node} node The removed node
21726         */
21727        "remove" : true,
21728        /**
21729         * @event move
21730         * Fires when this node is moved to a new location in the tree
21731         * @param {Tree} tree The owner tree
21732         * @param {Node} this This node
21733         * @param {Node} oldParent The old parent of this node
21734         * @param {Node} newParent The new parent of this node
21735         * @param {Number} index The index it was moved to
21736         */
21737        "move" : true,
21738        /**
21739         * @event insert
21740         * Fires when a new child node is inserted.
21741         * @param {Tree} tree The owner tree
21742         * @param {Node} this This node
21743         * @param {Node} node The child node inserted
21744         * @param {Node} refNode The child node the node was inserted before
21745         */
21746        "insert" : true,
21747        /**
21748         * @event beforeappend
21749         * Fires before a new child is appended, return false to cancel the append.
21750         * @param {Tree} tree The owner tree
21751         * @param {Node} this This node
21752         * @param {Node} node The child node to be appended
21753         */
21754        "beforeappend" : true,
21755        /**
21756         * @event beforeremove
21757         * Fires before a child is removed, return false to cancel the remove.
21758         * @param {Tree} tree The owner tree
21759         * @param {Node} this This node
21760         * @param {Node} node The child node to be removed
21761         */
21762        "beforeremove" : true,
21763        /**
21764         * @event beforemove
21765         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21766         * @param {Tree} tree The owner tree
21767         * @param {Node} this This node
21768         * @param {Node} oldParent The parent of this node
21769         * @param {Node} newParent The new parent this node is moving to
21770         * @param {Number} index The index it is being moved to
21771         */
21772        "beforemove" : true,
21773        /**
21774         * @event beforeinsert
21775         * Fires before a new child is inserted, return false to cancel the insert.
21776         * @param {Tree} tree The owner tree
21777         * @param {Node} this This node
21778         * @param {Node} node The child node to be inserted
21779         * @param {Node} refNode The child node the node is being inserted before
21780         */
21781        "beforeinsert" : true
21782    });
21783     this.listeners = this.attributes.listeners;
21784     Roo.data.Node.superclass.constructor.call(this);
21785 };
21786
21787 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21788     fireEvent : function(evtName){
21789         // first do standard event for this node
21790         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21791             return false;
21792         }
21793         // then bubble it up to the tree if the event wasn't cancelled
21794         var ot = this.getOwnerTree();
21795         if(ot){
21796             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21797                 return false;
21798             }
21799         }
21800         return true;
21801     },
21802
21803     /**
21804      * Returns true if this node is a leaf
21805      * @return {Boolean}
21806      */
21807     isLeaf : function(){
21808         return this.leaf === true;
21809     },
21810
21811     // private
21812     setFirstChild : function(node){
21813         this.firstChild = node;
21814     },
21815
21816     //private
21817     setLastChild : function(node){
21818         this.lastChild = node;
21819     },
21820
21821
21822     /**
21823      * Returns true if this node is the last child of its parent
21824      * @return {Boolean}
21825      */
21826     isLast : function(){
21827        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21828     },
21829
21830     /**
21831      * Returns true if this node is the first child of its parent
21832      * @return {Boolean}
21833      */
21834     isFirst : function(){
21835        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21836     },
21837
21838     hasChildNodes : function(){
21839         return !this.isLeaf() && this.childNodes.length > 0;
21840     },
21841
21842     /**
21843      * Insert node(s) as the last child node of this node.
21844      * @param {Node/Array} node The node or Array of nodes to append
21845      * @return {Node} The appended node if single append, or null if an array was passed
21846      */
21847     appendChild : function(node){
21848         var multi = false;
21849         if(node instanceof Array){
21850             multi = node;
21851         }else if(arguments.length > 1){
21852             multi = arguments;
21853         }
21854         // if passed an array or multiple args do them one by one
21855         if(multi){
21856             for(var i = 0, len = multi.length; i < len; i++) {
21857                 this.appendChild(multi[i]);
21858             }
21859         }else{
21860             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21861                 return false;
21862             }
21863             var index = this.childNodes.length;
21864             var oldParent = node.parentNode;
21865             // it's a move, make sure we move it cleanly
21866             if(oldParent){
21867                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21868                     return false;
21869                 }
21870                 oldParent.removeChild(node);
21871             }
21872             index = this.childNodes.length;
21873             if(index == 0){
21874                 this.setFirstChild(node);
21875             }
21876             this.childNodes.push(node);
21877             node.parentNode = this;
21878             var ps = this.childNodes[index-1];
21879             if(ps){
21880                 node.previousSibling = ps;
21881                 ps.nextSibling = node;
21882             }else{
21883                 node.previousSibling = null;
21884             }
21885             node.nextSibling = null;
21886             this.setLastChild(node);
21887             node.setOwnerTree(this.getOwnerTree());
21888             this.fireEvent("append", this.ownerTree, this, node, index);
21889             if(oldParent){
21890                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21891             }
21892             return node;
21893         }
21894     },
21895
21896     /**
21897      * Removes a child node from this node.
21898      * @param {Node} node The node to remove
21899      * @return {Node} The removed node
21900      */
21901     removeChild : function(node){
21902         var index = this.childNodes.indexOf(node);
21903         if(index == -1){
21904             return false;
21905         }
21906         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21907             return false;
21908         }
21909
21910         // remove it from childNodes collection
21911         this.childNodes.splice(index, 1);
21912
21913         // update siblings
21914         if(node.previousSibling){
21915             node.previousSibling.nextSibling = node.nextSibling;
21916         }
21917         if(node.nextSibling){
21918             node.nextSibling.previousSibling = node.previousSibling;
21919         }
21920
21921         // update child refs
21922         if(this.firstChild == node){
21923             this.setFirstChild(node.nextSibling);
21924         }
21925         if(this.lastChild == node){
21926             this.setLastChild(node.previousSibling);
21927         }
21928
21929         node.setOwnerTree(null);
21930         // clear any references from the node
21931         node.parentNode = null;
21932         node.previousSibling = null;
21933         node.nextSibling = null;
21934         this.fireEvent("remove", this.ownerTree, this, node);
21935         return node;
21936     },
21937
21938     /**
21939      * Inserts the first node before the second node in this nodes childNodes collection.
21940      * @param {Node} node The node to insert
21941      * @param {Node} refNode The node to insert before (if null the node is appended)
21942      * @return {Node} The inserted node
21943      */
21944     insertBefore : function(node, refNode){
21945         if(!refNode){ // like standard Dom, refNode can be null for append
21946             return this.appendChild(node);
21947         }
21948         // nothing to do
21949         if(node == refNode){
21950             return false;
21951         }
21952
21953         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21954             return false;
21955         }
21956         var index = this.childNodes.indexOf(refNode);
21957         var oldParent = node.parentNode;
21958         var refIndex = index;
21959
21960         // when moving internally, indexes will change after remove
21961         if(oldParent == this && this.childNodes.indexOf(node) < index){
21962             refIndex--;
21963         }
21964
21965         // it's a move, make sure we move it cleanly
21966         if(oldParent){
21967             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21968                 return false;
21969             }
21970             oldParent.removeChild(node);
21971         }
21972         if(refIndex == 0){
21973             this.setFirstChild(node);
21974         }
21975         this.childNodes.splice(refIndex, 0, node);
21976         node.parentNode = this;
21977         var ps = this.childNodes[refIndex-1];
21978         if(ps){
21979             node.previousSibling = ps;
21980             ps.nextSibling = node;
21981         }else{
21982             node.previousSibling = null;
21983         }
21984         node.nextSibling = refNode;
21985         refNode.previousSibling = node;
21986         node.setOwnerTree(this.getOwnerTree());
21987         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21988         if(oldParent){
21989             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21990         }
21991         return node;
21992     },
21993
21994     /**
21995      * Returns the child node at the specified index.
21996      * @param {Number} index
21997      * @return {Node}
21998      */
21999     item : function(index){
22000         return this.childNodes[index];
22001     },
22002
22003     /**
22004      * Replaces one child node in this node with another.
22005      * @param {Node} newChild The replacement node
22006      * @param {Node} oldChild The node to replace
22007      * @return {Node} The replaced node
22008      */
22009     replaceChild : function(newChild, oldChild){
22010         this.insertBefore(newChild, oldChild);
22011         this.removeChild(oldChild);
22012         return oldChild;
22013     },
22014
22015     /**
22016      * Returns the index of a child node
22017      * @param {Node} node
22018      * @return {Number} The index of the node or -1 if it was not found
22019      */
22020     indexOf : function(child){
22021         return this.childNodes.indexOf(child);
22022     },
22023
22024     /**
22025      * Returns the tree this node is in.
22026      * @return {Tree}
22027      */
22028     getOwnerTree : function(){
22029         // if it doesn't have one, look for one
22030         if(!this.ownerTree){
22031             var p = this;
22032             while(p){
22033                 if(p.ownerTree){
22034                     this.ownerTree = p.ownerTree;
22035                     break;
22036                 }
22037                 p = p.parentNode;
22038             }
22039         }
22040         return this.ownerTree;
22041     },
22042
22043     /**
22044      * Returns depth of this node (the root node has a depth of 0)
22045      * @return {Number}
22046      */
22047     getDepth : function(){
22048         var depth = 0;
22049         var p = this;
22050         while(p.parentNode){
22051             ++depth;
22052             p = p.parentNode;
22053         }
22054         return depth;
22055     },
22056
22057     // private
22058     setOwnerTree : function(tree){
22059         // if it's move, we need to update everyone
22060         if(tree != this.ownerTree){
22061             if(this.ownerTree){
22062                 this.ownerTree.unregisterNode(this);
22063             }
22064             this.ownerTree = tree;
22065             var cs = this.childNodes;
22066             for(var i = 0, len = cs.length; i < len; i++) {
22067                 cs[i].setOwnerTree(tree);
22068             }
22069             if(tree){
22070                 tree.registerNode(this);
22071             }
22072         }
22073     },
22074
22075     /**
22076      * Returns the path for this node. The path can be used to expand or select this node programmatically.
22077      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
22078      * @return {String} The path
22079      */
22080     getPath : function(attr){
22081         attr = attr || "id";
22082         var p = this.parentNode;
22083         var b = [this.attributes[attr]];
22084         while(p){
22085             b.unshift(p.attributes[attr]);
22086             p = p.parentNode;
22087         }
22088         var sep = this.getOwnerTree().pathSeparator;
22089         return sep + b.join(sep);
22090     },
22091
22092     /**
22093      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22094      * function call will be the scope provided or the current node. The arguments to the function
22095      * will be the args provided or the current node. If the function returns false at any point,
22096      * the bubble is stopped.
22097      * @param {Function} fn The function to call
22098      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22099      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22100      */
22101     bubble : function(fn, scope, args){
22102         var p = this;
22103         while(p){
22104             if(fn.call(scope || p, args || p) === false){
22105                 break;
22106             }
22107             p = p.parentNode;
22108         }
22109     },
22110
22111     /**
22112      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22113      * function call will be the scope provided or the current node. The arguments to the function
22114      * will be the args provided or the current node. If the function returns false at any point,
22115      * the cascade is stopped on that branch.
22116      * @param {Function} fn The function to call
22117      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22118      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22119      */
22120     cascade : function(fn, scope, args){
22121         if(fn.call(scope || this, args || this) !== false){
22122             var cs = this.childNodes;
22123             for(var i = 0, len = cs.length; i < len; i++) {
22124                 cs[i].cascade(fn, scope, args);
22125             }
22126         }
22127     },
22128
22129     /**
22130      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
22131      * function call will be the scope provided or the current node. The arguments to the function
22132      * will be the args provided or the current node. If the function returns false at any point,
22133      * the iteration stops.
22134      * @param {Function} fn The function to call
22135      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22136      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22137      */
22138     eachChild : function(fn, scope, args){
22139         var cs = this.childNodes;
22140         for(var i = 0, len = cs.length; i < len; i++) {
22141                 if(fn.call(scope || this, args || cs[i]) === false){
22142                     break;
22143                 }
22144         }
22145     },
22146
22147     /**
22148      * Finds the first child that has the attribute with the specified value.
22149      * @param {String} attribute The attribute name
22150      * @param {Mixed} value The value to search for
22151      * @return {Node} The found child or null if none was found
22152      */
22153     findChild : function(attribute, value){
22154         var cs = this.childNodes;
22155         for(var i = 0, len = cs.length; i < len; i++) {
22156                 if(cs[i].attributes[attribute] == value){
22157                     return cs[i];
22158                 }
22159         }
22160         return null;
22161     },
22162
22163     /**
22164      * Finds the first child by a custom function. The child matches if the function passed
22165      * returns true.
22166      * @param {Function} fn
22167      * @param {Object} scope (optional)
22168      * @return {Node} The found child or null if none was found
22169      */
22170     findChildBy : function(fn, scope){
22171         var cs = this.childNodes;
22172         for(var i = 0, len = cs.length; i < len; i++) {
22173                 if(fn.call(scope||cs[i], cs[i]) === true){
22174                     return cs[i];
22175                 }
22176         }
22177         return null;
22178     },
22179
22180     /**
22181      * Sorts this nodes children using the supplied sort function
22182      * @param {Function} fn
22183      * @param {Object} scope (optional)
22184      */
22185     sort : function(fn, scope){
22186         var cs = this.childNodes;
22187         var len = cs.length;
22188         if(len > 0){
22189             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
22190             cs.sort(sortFn);
22191             for(var i = 0; i < len; i++){
22192                 var n = cs[i];
22193                 n.previousSibling = cs[i-1];
22194                 n.nextSibling = cs[i+1];
22195                 if(i == 0){
22196                     this.setFirstChild(n);
22197                 }
22198                 if(i == len-1){
22199                     this.setLastChild(n);
22200                 }
22201             }
22202         }
22203     },
22204
22205     /**
22206      * Returns true if this node is an ancestor (at any point) of the passed node.
22207      * @param {Node} node
22208      * @return {Boolean}
22209      */
22210     contains : function(node){
22211         return node.isAncestor(this);
22212     },
22213
22214     /**
22215      * Returns true if the passed node is an ancestor (at any point) of this node.
22216      * @param {Node} node
22217      * @return {Boolean}
22218      */
22219     isAncestor : function(node){
22220         var p = this.parentNode;
22221         while(p){
22222             if(p == node){
22223                 return true;
22224             }
22225             p = p.parentNode;
22226         }
22227         return false;
22228     },
22229
22230     toString : function(){
22231         return "[Node"+(this.id?" "+this.id:"")+"]";
22232     }
22233 });/*
22234  * Based on:
22235  * Ext JS Library 1.1.1
22236  * Copyright(c) 2006-2007, Ext JS, LLC.
22237  *
22238  * Originally Released Under LGPL - original licence link has changed is not relivant.
22239  *
22240  * Fork - LGPL
22241  * <script type="text/javascript">
22242  */
22243  
22244
22245 /**
22246  * @class Roo.ComponentMgr
22247  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
22248  * @singleton
22249  */
22250 Roo.ComponentMgr = function(){
22251     var all = new Roo.util.MixedCollection();
22252
22253     return {
22254         /**
22255          * Registers a component.
22256          * @param {Roo.Component} c The component
22257          */
22258         register : function(c){
22259             all.add(c);
22260         },
22261
22262         /**
22263          * Unregisters a component.
22264          * @param {Roo.Component} c The component
22265          */
22266         unregister : function(c){
22267             all.remove(c);
22268         },
22269
22270         /**
22271          * Returns a component by id
22272          * @param {String} id The component id
22273          */
22274         get : function(id){
22275             return all.get(id);
22276         },
22277
22278         /**
22279          * Registers a function that will be called when a specified component is added to ComponentMgr
22280          * @param {String} id The component id
22281          * @param {Funtction} fn The callback function
22282          * @param {Object} scope The scope of the callback
22283          */
22284         onAvailable : function(id, fn, scope){
22285             all.on("add", function(index, o){
22286                 if(o.id == id){
22287                     fn.call(scope || o, o);
22288                     all.un("add", fn, scope);
22289                 }
22290             });
22291         }
22292     };
22293 }();/*
22294  * Based on:
22295  * Ext JS Library 1.1.1
22296  * Copyright(c) 2006-2007, Ext JS, LLC.
22297  *
22298  * Originally Released Under LGPL - original licence link has changed is not relivant.
22299  *
22300  * Fork - LGPL
22301  * <script type="text/javascript">
22302  */
22303  
22304 /**
22305  * @class Roo.Component
22306  * @extends Roo.util.Observable
22307  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
22308  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
22309  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
22310  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
22311  * All visual components (widgets) that require rendering into a layout should subclass Component.
22312  * @constructor
22313  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
22314  * 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
22315  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
22316  */
22317 Roo.Component = function(config){
22318     config = config || {};
22319     if(config.tagName || config.dom || typeof config == "string"){ // element object
22320         config = {el: config, id: config.id || config};
22321     }
22322     this.initialConfig = config;
22323
22324     Roo.apply(this, config);
22325     this.addEvents({
22326         /**
22327          * @event disable
22328          * Fires after the component is disabled.
22329              * @param {Roo.Component} this
22330              */
22331         disable : true,
22332         /**
22333          * @event enable
22334          * Fires after the component is enabled.
22335              * @param {Roo.Component} this
22336              */
22337         enable : true,
22338         /**
22339          * @event beforeshow
22340          * Fires before the component is shown.  Return false to stop the show.
22341              * @param {Roo.Component} this
22342              */
22343         beforeshow : true,
22344         /**
22345          * @event show
22346          * Fires after the component is shown.
22347              * @param {Roo.Component} this
22348              */
22349         show : true,
22350         /**
22351          * @event beforehide
22352          * Fires before the component is hidden. Return false to stop the hide.
22353              * @param {Roo.Component} this
22354              */
22355         beforehide : true,
22356         /**
22357          * @event hide
22358          * Fires after the component is hidden.
22359              * @param {Roo.Component} this
22360              */
22361         hide : true,
22362         /**
22363          * @event beforerender
22364          * Fires before the component is rendered. Return false to stop the render.
22365              * @param {Roo.Component} this
22366              */
22367         beforerender : true,
22368         /**
22369          * @event render
22370          * Fires after the component is rendered.
22371              * @param {Roo.Component} this
22372              */
22373         render : true,
22374         /**
22375          * @event beforedestroy
22376          * Fires before the component is destroyed. Return false to stop the destroy.
22377              * @param {Roo.Component} this
22378              */
22379         beforedestroy : true,
22380         /**
22381          * @event destroy
22382          * Fires after the component is destroyed.
22383              * @param {Roo.Component} this
22384              */
22385         destroy : true
22386     });
22387     if(!this.id){
22388         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
22389     }
22390     Roo.ComponentMgr.register(this);
22391     Roo.Component.superclass.constructor.call(this);
22392     this.initComponent();
22393     if(this.renderTo){ // not supported by all components yet. use at your own risk!
22394         this.render(this.renderTo);
22395         delete this.renderTo;
22396     }
22397 };
22398
22399 /** @private */
22400 Roo.Component.AUTO_ID = 1000;
22401
22402 Roo.extend(Roo.Component, Roo.util.Observable, {
22403     /**
22404      * @scope Roo.Component.prototype
22405      * @type {Boolean}
22406      * true if this component is hidden. Read-only.
22407      */
22408     hidden : false,
22409     /**
22410      * @type {Boolean}
22411      * true if this component is disabled. Read-only.
22412      */
22413     disabled : false,
22414     /**
22415      * @type {Boolean}
22416      * true if this component has been rendered. Read-only.
22417      */
22418     rendered : false,
22419     
22420     /** @cfg {String} disableClass
22421      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
22422      */
22423     disabledClass : "x-item-disabled",
22424         /** @cfg {Boolean} allowDomMove
22425          * Whether the component can move the Dom node when rendering (defaults to true).
22426          */
22427     allowDomMove : true,
22428     /** @cfg {String} hideMode
22429      * How this component should hidden. Supported values are
22430      * "visibility" (css visibility), "offsets" (negative offset position) and
22431      * "display" (css display) - defaults to "display".
22432      */
22433     hideMode: 'display',
22434
22435     /** @private */
22436     ctype : "Roo.Component",
22437
22438     /**
22439      * @cfg {String} actionMode 
22440      * which property holds the element that used for  hide() / show() / disable() / enable()
22441      * default is 'el' 
22442      */
22443     actionMode : "el",
22444
22445     /** @private */
22446     getActionEl : function(){
22447         return this[this.actionMode];
22448     },
22449
22450     initComponent : Roo.emptyFn,
22451     /**
22452      * If this is a lazy rendering component, render it to its container element.
22453      * @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.
22454      */
22455     render : function(container, position){
22456         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
22457             if(!container && this.el){
22458                 this.el = Roo.get(this.el);
22459                 container = this.el.dom.parentNode;
22460                 this.allowDomMove = false;
22461             }
22462             this.container = Roo.get(container);
22463             this.rendered = true;
22464             if(position !== undefined){
22465                 if(typeof position == 'number'){
22466                     position = this.container.dom.childNodes[position];
22467                 }else{
22468                     position = Roo.getDom(position);
22469                 }
22470             }
22471             this.onRender(this.container, position || null);
22472             if(this.cls){
22473                 this.el.addClass(this.cls);
22474                 delete this.cls;
22475             }
22476             if(this.style){
22477                 this.el.applyStyles(this.style);
22478                 delete this.style;
22479             }
22480             this.fireEvent("render", this);
22481             this.afterRender(this.container);
22482             if(this.hidden){
22483                 this.hide();
22484             }
22485             if(this.disabled){
22486                 this.disable();
22487             }
22488         }
22489         return this;
22490     },
22491
22492     /** @private */
22493     // default function is not really useful
22494     onRender : function(ct, position){
22495         if(this.el){
22496             this.el = Roo.get(this.el);
22497             if(this.allowDomMove !== false){
22498                 ct.dom.insertBefore(this.el.dom, position);
22499             }
22500         }
22501     },
22502
22503     /** @private */
22504     getAutoCreate : function(){
22505         var cfg = typeof this.autoCreate == "object" ?
22506                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
22507         if(this.id && !cfg.id){
22508             cfg.id = this.id;
22509         }
22510         return cfg;
22511     },
22512
22513     /** @private */
22514     afterRender : Roo.emptyFn,
22515
22516     /**
22517      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22518      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22519      */
22520     destroy : function(){
22521         if(this.fireEvent("beforedestroy", this) !== false){
22522             this.purgeListeners();
22523             this.beforeDestroy();
22524             if(this.rendered){
22525                 this.el.removeAllListeners();
22526                 this.el.remove();
22527                 if(this.actionMode == "container"){
22528                     this.container.remove();
22529                 }
22530             }
22531             this.onDestroy();
22532             Roo.ComponentMgr.unregister(this);
22533             this.fireEvent("destroy", this);
22534         }
22535     },
22536
22537         /** @private */
22538     beforeDestroy : function(){
22539
22540     },
22541
22542         /** @private */
22543         onDestroy : function(){
22544
22545     },
22546
22547     /**
22548      * Returns the underlying {@link Roo.Element}.
22549      * @return {Roo.Element} The element
22550      */
22551     getEl : function(){
22552         return this.el;
22553     },
22554
22555     /**
22556      * Returns the id of this component.
22557      * @return {String}
22558      */
22559     getId : function(){
22560         return this.id;
22561     },
22562
22563     /**
22564      * Try to focus this component.
22565      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22566      * @return {Roo.Component} this
22567      */
22568     focus : function(selectText){
22569         if(this.rendered){
22570             this.el.focus();
22571             if(selectText === true){
22572                 this.el.dom.select();
22573             }
22574         }
22575         return this;
22576     },
22577
22578     /** @private */
22579     blur : function(){
22580         if(this.rendered){
22581             this.el.blur();
22582         }
22583         return this;
22584     },
22585
22586     /**
22587      * Disable this component.
22588      * @return {Roo.Component} this
22589      */
22590     disable : function(){
22591         if(this.rendered){
22592             this.onDisable();
22593         }
22594         this.disabled = true;
22595         this.fireEvent("disable", this);
22596         return this;
22597     },
22598
22599         // private
22600     onDisable : function(){
22601         this.getActionEl().addClass(this.disabledClass);
22602         this.el.dom.disabled = true;
22603     },
22604
22605     /**
22606      * Enable this component.
22607      * @return {Roo.Component} this
22608      */
22609     enable : function(){
22610         if(this.rendered){
22611             this.onEnable();
22612         }
22613         this.disabled = false;
22614         this.fireEvent("enable", this);
22615         return this;
22616     },
22617
22618         // private
22619     onEnable : function(){
22620         this.getActionEl().removeClass(this.disabledClass);
22621         this.el.dom.disabled = false;
22622     },
22623
22624     /**
22625      * Convenience function for setting disabled/enabled by boolean.
22626      * @param {Boolean} disabled
22627      */
22628     setDisabled : function(disabled){
22629         this[disabled ? "disable" : "enable"]();
22630     },
22631
22632     /**
22633      * Show this component.
22634      * @return {Roo.Component} this
22635      */
22636     show: function(){
22637         if(this.fireEvent("beforeshow", this) !== false){
22638             this.hidden = false;
22639             if(this.rendered){
22640                 this.onShow();
22641             }
22642             this.fireEvent("show", this);
22643         }
22644         return this;
22645     },
22646
22647     // private
22648     onShow : function(){
22649         var ae = this.getActionEl();
22650         if(this.hideMode == 'visibility'){
22651             ae.dom.style.visibility = "visible";
22652         }else if(this.hideMode == 'offsets'){
22653             ae.removeClass('x-hidden');
22654         }else{
22655             ae.dom.style.display = "";
22656         }
22657     },
22658
22659     /**
22660      * Hide this component.
22661      * @return {Roo.Component} this
22662      */
22663     hide: function(){
22664         if(this.fireEvent("beforehide", this) !== false){
22665             this.hidden = true;
22666             if(this.rendered){
22667                 this.onHide();
22668             }
22669             this.fireEvent("hide", this);
22670         }
22671         return this;
22672     },
22673
22674     // private
22675     onHide : function(){
22676         var ae = this.getActionEl();
22677         if(this.hideMode == 'visibility'){
22678             ae.dom.style.visibility = "hidden";
22679         }else if(this.hideMode == 'offsets'){
22680             ae.addClass('x-hidden');
22681         }else{
22682             ae.dom.style.display = "none";
22683         }
22684     },
22685
22686     /**
22687      * Convenience function to hide or show this component by boolean.
22688      * @param {Boolean} visible True to show, false to hide
22689      * @return {Roo.Component} this
22690      */
22691     setVisible: function(visible){
22692         if(visible) {
22693             this.show();
22694         }else{
22695             this.hide();
22696         }
22697         return this;
22698     },
22699
22700     /**
22701      * Returns true if this component is visible.
22702      */
22703     isVisible : function(){
22704         return this.getActionEl().isVisible();
22705     },
22706
22707     cloneConfig : function(overrides){
22708         overrides = overrides || {};
22709         var id = overrides.id || Roo.id();
22710         var cfg = Roo.applyIf(overrides, this.initialConfig);
22711         cfg.id = id; // prevent dup id
22712         return new this.constructor(cfg);
22713     }
22714 });/*
22715  * Based on:
22716  * Ext JS Library 1.1.1
22717  * Copyright(c) 2006-2007, Ext JS, LLC.
22718  *
22719  * Originally Released Under LGPL - original licence link has changed is not relivant.
22720  *
22721  * Fork - LGPL
22722  * <script type="text/javascript">
22723  */
22724  (function(){ 
22725 /**
22726  * @class Roo.Layer
22727  * @extends Roo.Element
22728  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22729  * automatic maintaining of shadow/shim positions.
22730  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22731  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22732  * you can pass a string with a CSS class name. False turns off the shadow.
22733  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22734  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22735  * @cfg {String} cls CSS class to add to the element
22736  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22737  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22738  * @constructor
22739  * @param {Object} config An object with config options.
22740  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22741  */
22742
22743 Roo.Layer = function(config, existingEl){
22744     config = config || {};
22745     var dh = Roo.DomHelper;
22746     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22747     if(existingEl){
22748         this.dom = Roo.getDom(existingEl);
22749     }
22750     if(!this.dom){
22751         var o = config.dh || {tag: "div", cls: "x-layer"};
22752         this.dom = dh.append(pel, o);
22753     }
22754     if(config.cls){
22755         this.addClass(config.cls);
22756     }
22757     this.constrain = config.constrain !== false;
22758     this.visibilityMode = Roo.Element.VISIBILITY;
22759     if(config.id){
22760         this.id = this.dom.id = config.id;
22761     }else{
22762         this.id = Roo.id(this.dom);
22763     }
22764     this.zindex = config.zindex || this.getZIndex();
22765     this.position("absolute", this.zindex);
22766     if(config.shadow){
22767         this.shadowOffset = config.shadowOffset || 4;
22768         this.shadow = new Roo.Shadow({
22769             offset : this.shadowOffset,
22770             mode : config.shadow
22771         });
22772     }else{
22773         this.shadowOffset = 0;
22774     }
22775     this.useShim = config.shim !== false && Roo.useShims;
22776     this.useDisplay = config.useDisplay;
22777     this.hide();
22778 };
22779
22780 var supr = Roo.Element.prototype;
22781
22782 // shims are shared among layer to keep from having 100 iframes
22783 var shims = [];
22784
22785 Roo.extend(Roo.Layer, Roo.Element, {
22786
22787     getZIndex : function(){
22788         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22789     },
22790
22791     getShim : function(){
22792         if(!this.useShim){
22793             return null;
22794         }
22795         if(this.shim){
22796             return this.shim;
22797         }
22798         var shim = shims.shift();
22799         if(!shim){
22800             shim = this.createShim();
22801             shim.enableDisplayMode('block');
22802             shim.dom.style.display = 'none';
22803             shim.dom.style.visibility = 'visible';
22804         }
22805         var pn = this.dom.parentNode;
22806         if(shim.dom.parentNode != pn){
22807             pn.insertBefore(shim.dom, this.dom);
22808         }
22809         shim.setStyle('z-index', this.getZIndex()-2);
22810         this.shim = shim;
22811         return shim;
22812     },
22813
22814     hideShim : function(){
22815         if(this.shim){
22816             this.shim.setDisplayed(false);
22817             shims.push(this.shim);
22818             delete this.shim;
22819         }
22820     },
22821
22822     disableShadow : function(){
22823         if(this.shadow){
22824             this.shadowDisabled = true;
22825             this.shadow.hide();
22826             this.lastShadowOffset = this.shadowOffset;
22827             this.shadowOffset = 0;
22828         }
22829     },
22830
22831     enableShadow : function(show){
22832         if(this.shadow){
22833             this.shadowDisabled = false;
22834             this.shadowOffset = this.lastShadowOffset;
22835             delete this.lastShadowOffset;
22836             if(show){
22837                 this.sync(true);
22838             }
22839         }
22840     },
22841
22842     // private
22843     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22844     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22845     sync : function(doShow){
22846         var sw = this.shadow;
22847         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22848             var sh = this.getShim();
22849
22850             var w = this.getWidth(),
22851                 h = this.getHeight();
22852
22853             var l = this.getLeft(true),
22854                 t = this.getTop(true);
22855
22856             if(sw && !this.shadowDisabled){
22857                 if(doShow && !sw.isVisible()){
22858                     sw.show(this);
22859                 }else{
22860                     sw.realign(l, t, w, h);
22861                 }
22862                 if(sh){
22863                     if(doShow){
22864                        sh.show();
22865                     }
22866                     // fit the shim behind the shadow, so it is shimmed too
22867                     var a = sw.adjusts, s = sh.dom.style;
22868                     s.left = (Math.min(l, l+a.l))+"px";
22869                     s.top = (Math.min(t, t+a.t))+"px";
22870                     s.width = (w+a.w)+"px";
22871                     s.height = (h+a.h)+"px";
22872                 }
22873             }else if(sh){
22874                 if(doShow){
22875                    sh.show();
22876                 }
22877                 sh.setSize(w, h);
22878                 sh.setLeftTop(l, t);
22879             }
22880             
22881         }
22882     },
22883
22884     // private
22885     destroy : function(){
22886         this.hideShim();
22887         if(this.shadow){
22888             this.shadow.hide();
22889         }
22890         this.removeAllListeners();
22891         var pn = this.dom.parentNode;
22892         if(pn){
22893             pn.removeChild(this.dom);
22894         }
22895         Roo.Element.uncache(this.id);
22896     },
22897
22898     remove : function(){
22899         this.destroy();
22900     },
22901
22902     // private
22903     beginUpdate : function(){
22904         this.updating = true;
22905     },
22906
22907     // private
22908     endUpdate : function(){
22909         this.updating = false;
22910         this.sync(true);
22911     },
22912
22913     // private
22914     hideUnders : function(negOffset){
22915         if(this.shadow){
22916             this.shadow.hide();
22917         }
22918         this.hideShim();
22919     },
22920
22921     // private
22922     constrainXY : function(){
22923         if(this.constrain){
22924             var vw = Roo.lib.Dom.getViewWidth(),
22925                 vh = Roo.lib.Dom.getViewHeight();
22926             var s = Roo.get(document).getScroll();
22927
22928             var xy = this.getXY();
22929             var x = xy[0], y = xy[1];   
22930             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22931             // only move it if it needs it
22932             var moved = false;
22933             // first validate right/bottom
22934             if((x + w) > vw+s.left){
22935                 x = vw - w - this.shadowOffset;
22936                 moved = true;
22937             }
22938             if((y + h) > vh+s.top){
22939                 y = vh - h - this.shadowOffset;
22940                 moved = true;
22941             }
22942             // then make sure top/left isn't negative
22943             if(x < s.left){
22944                 x = s.left;
22945                 moved = true;
22946             }
22947             if(y < s.top){
22948                 y = s.top;
22949                 moved = true;
22950             }
22951             if(moved){
22952                 if(this.avoidY){
22953                     var ay = this.avoidY;
22954                     if(y <= ay && (y+h) >= ay){
22955                         y = ay-h-5;   
22956                     }
22957                 }
22958                 xy = [x, y];
22959                 this.storeXY(xy);
22960                 supr.setXY.call(this, xy);
22961                 this.sync();
22962             }
22963         }
22964     },
22965
22966     isVisible : function(){
22967         return this.visible;    
22968     },
22969
22970     // private
22971     showAction : function(){
22972         this.visible = true; // track visibility to prevent getStyle calls
22973         if(this.useDisplay === true){
22974             this.setDisplayed("");
22975         }else if(this.lastXY){
22976             supr.setXY.call(this, this.lastXY);
22977         }else if(this.lastLT){
22978             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22979         }
22980     },
22981
22982     // private
22983     hideAction : function(){
22984         this.visible = false;
22985         if(this.useDisplay === true){
22986             this.setDisplayed(false);
22987         }else{
22988             this.setLeftTop(-10000,-10000);
22989         }
22990     },
22991
22992     // overridden Element method
22993     setVisible : function(v, a, d, c, e){
22994         if(v){
22995             this.showAction();
22996         }
22997         if(a && v){
22998             var cb = function(){
22999                 this.sync(true);
23000                 if(c){
23001                     c();
23002                 }
23003             }.createDelegate(this);
23004             supr.setVisible.call(this, true, true, d, cb, e);
23005         }else{
23006             if(!v){
23007                 this.hideUnders(true);
23008             }
23009             var cb = c;
23010             if(a){
23011                 cb = function(){
23012                     this.hideAction();
23013                     if(c){
23014                         c();
23015                     }
23016                 }.createDelegate(this);
23017             }
23018             supr.setVisible.call(this, v, a, d, cb, e);
23019             if(v){
23020                 this.sync(true);
23021             }else if(!a){
23022                 this.hideAction();
23023             }
23024         }
23025     },
23026
23027     storeXY : function(xy){
23028         delete this.lastLT;
23029         this.lastXY = xy;
23030     },
23031
23032     storeLeftTop : function(left, top){
23033         delete this.lastXY;
23034         this.lastLT = [left, top];
23035     },
23036
23037     // private
23038     beforeFx : function(){
23039         this.beforeAction();
23040         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23041     },
23042
23043     // private
23044     afterFx : function(){
23045         Roo.Layer.superclass.afterFx.apply(this, arguments);
23046         this.sync(this.isVisible());
23047     },
23048
23049     // private
23050     beforeAction : function(){
23051         if(!this.updating && this.shadow){
23052             this.shadow.hide();
23053         }
23054     },
23055
23056     // overridden Element method
23057     setLeft : function(left){
23058         this.storeLeftTop(left, this.getTop(true));
23059         supr.setLeft.apply(this, arguments);
23060         this.sync();
23061     },
23062
23063     setTop : function(top){
23064         this.storeLeftTop(this.getLeft(true), top);
23065         supr.setTop.apply(this, arguments);
23066         this.sync();
23067     },
23068
23069     setLeftTop : function(left, top){
23070         this.storeLeftTop(left, top);
23071         supr.setLeftTop.apply(this, arguments);
23072         this.sync();
23073     },
23074
23075     setXY : function(xy, a, d, c, e){
23076         this.fixDisplay();
23077         this.beforeAction();
23078         this.storeXY(xy);
23079         var cb = this.createCB(c);
23080         supr.setXY.call(this, xy, a, d, cb, e);
23081         if(!a){
23082             cb();
23083         }
23084     },
23085
23086     // private
23087     createCB : function(c){
23088         var el = this;
23089         return function(){
23090             el.constrainXY();
23091             el.sync(true);
23092             if(c){
23093                 c();
23094             }
23095         };
23096     },
23097
23098     // overridden Element method
23099     setX : function(x, a, d, c, e){
23100         this.setXY([x, this.getY()], a, d, c, e);
23101     },
23102
23103     // overridden Element method
23104     setY : function(y, a, d, c, e){
23105         this.setXY([this.getX(), y], a, d, c, e);
23106     },
23107
23108     // overridden Element method
23109     setSize : function(w, h, a, d, c, e){
23110         this.beforeAction();
23111         var cb = this.createCB(c);
23112         supr.setSize.call(this, w, h, a, d, cb, e);
23113         if(!a){
23114             cb();
23115         }
23116     },
23117
23118     // overridden Element method
23119     setWidth : function(w, a, d, c, e){
23120         this.beforeAction();
23121         var cb = this.createCB(c);
23122         supr.setWidth.call(this, w, a, d, cb, e);
23123         if(!a){
23124             cb();
23125         }
23126     },
23127
23128     // overridden Element method
23129     setHeight : function(h, a, d, c, e){
23130         this.beforeAction();
23131         var cb = this.createCB(c);
23132         supr.setHeight.call(this, h, a, d, cb, e);
23133         if(!a){
23134             cb();
23135         }
23136     },
23137
23138     // overridden Element method
23139     setBounds : function(x, y, w, h, a, d, c, e){
23140         this.beforeAction();
23141         var cb = this.createCB(c);
23142         if(!a){
23143             this.storeXY([x, y]);
23144             supr.setXY.call(this, [x, y]);
23145             supr.setSize.call(this, w, h, a, d, cb, e);
23146             cb();
23147         }else{
23148             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
23149         }
23150         return this;
23151     },
23152     
23153     /**
23154      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
23155      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
23156      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
23157      * @param {Number} zindex The new z-index to set
23158      * @return {this} The Layer
23159      */
23160     setZIndex : function(zindex){
23161         this.zindex = zindex;
23162         this.setStyle("z-index", zindex + 2);
23163         if(this.shadow){
23164             this.shadow.setZIndex(zindex + 1);
23165         }
23166         if(this.shim){
23167             this.shim.setStyle("z-index", zindex);
23168         }
23169     }
23170 });
23171 })();/*
23172  * Based on:
23173  * Ext JS Library 1.1.1
23174  * Copyright(c) 2006-2007, Ext JS, LLC.
23175  *
23176  * Originally Released Under LGPL - original licence link has changed is not relivant.
23177  *
23178  * Fork - LGPL
23179  * <script type="text/javascript">
23180  */
23181
23182
23183 /**
23184  * @class Roo.Shadow
23185  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
23186  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
23187  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
23188  * @constructor
23189  * Create a new Shadow
23190  * @param {Object} config The config object
23191  */
23192 Roo.Shadow = function(config){
23193     Roo.apply(this, config);
23194     if(typeof this.mode != "string"){
23195         this.mode = this.defaultMode;
23196     }
23197     var o = this.offset, a = {h: 0};
23198     var rad = Math.floor(this.offset/2);
23199     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
23200         case "drop":
23201             a.w = 0;
23202             a.l = a.t = o;
23203             a.t -= 1;
23204             if(Roo.isIE){
23205                 a.l -= this.offset + rad;
23206                 a.t -= this.offset + rad;
23207                 a.w -= rad;
23208                 a.h -= rad;
23209                 a.t += 1;
23210             }
23211         break;
23212         case "sides":
23213             a.w = (o*2);
23214             a.l = -o;
23215             a.t = o-1;
23216             if(Roo.isIE){
23217                 a.l -= (this.offset - rad);
23218                 a.t -= this.offset + rad;
23219                 a.l += 1;
23220                 a.w -= (this.offset - rad)*2;
23221                 a.w -= rad + 1;
23222                 a.h -= 1;
23223             }
23224         break;
23225         case "frame":
23226             a.w = a.h = (o*2);
23227             a.l = a.t = -o;
23228             a.t += 1;
23229             a.h -= 2;
23230             if(Roo.isIE){
23231                 a.l -= (this.offset - rad);
23232                 a.t -= (this.offset - rad);
23233                 a.l += 1;
23234                 a.w -= (this.offset + rad + 1);
23235                 a.h -= (this.offset + rad);
23236                 a.h += 1;
23237             }
23238         break;
23239     };
23240
23241     this.adjusts = a;
23242 };
23243
23244 Roo.Shadow.prototype = {
23245     /**
23246      * @cfg {String} mode
23247      * The shadow display mode.  Supports the following options:<br />
23248      * sides: Shadow displays on both sides and bottom only<br />
23249      * frame: Shadow displays equally on all four sides<br />
23250      * drop: Traditional bottom-right drop shadow (default)
23251      */
23252     /**
23253      * @cfg {String} offset
23254      * The number of pixels to offset the shadow from the element (defaults to 4)
23255      */
23256     offset: 4,
23257
23258     // private
23259     defaultMode: "drop",
23260
23261     /**
23262      * Displays the shadow under the target element
23263      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
23264      */
23265     show : function(target){
23266         target = Roo.get(target);
23267         if(!this.el){
23268             this.el = Roo.Shadow.Pool.pull();
23269             if(this.el.dom.nextSibling != target.dom){
23270                 this.el.insertBefore(target);
23271             }
23272         }
23273         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
23274         if(Roo.isIE){
23275             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
23276         }
23277         this.realign(
23278             target.getLeft(true),
23279             target.getTop(true),
23280             target.getWidth(),
23281             target.getHeight()
23282         );
23283         this.el.dom.style.display = "block";
23284     },
23285
23286     /**
23287      * Returns true if the shadow is visible, else false
23288      */
23289     isVisible : function(){
23290         return this.el ? true : false;  
23291     },
23292
23293     /**
23294      * Direct alignment when values are already available. Show must be called at least once before
23295      * calling this method to ensure it is initialized.
23296      * @param {Number} left The target element left position
23297      * @param {Number} top The target element top position
23298      * @param {Number} width The target element width
23299      * @param {Number} height The target element height
23300      */
23301     realign : function(l, t, w, h){
23302         if(!this.el){
23303             return;
23304         }
23305         var a = this.adjusts, d = this.el.dom, s = d.style;
23306         var iea = 0;
23307         s.left = (l+a.l)+"px";
23308         s.top = (t+a.t)+"px";
23309         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
23310  
23311         if(s.width != sws || s.height != shs){
23312             s.width = sws;
23313             s.height = shs;
23314             if(!Roo.isIE){
23315                 var cn = d.childNodes;
23316                 var sww = Math.max(0, (sw-12))+"px";
23317                 cn[0].childNodes[1].style.width = sww;
23318                 cn[1].childNodes[1].style.width = sww;
23319                 cn[2].childNodes[1].style.width = sww;
23320                 cn[1].style.height = Math.max(0, (sh-12))+"px";
23321             }
23322         }
23323     },
23324
23325     /**
23326      * Hides this shadow
23327      */
23328     hide : function(){
23329         if(this.el){
23330             this.el.dom.style.display = "none";
23331             Roo.Shadow.Pool.push(this.el);
23332             delete this.el;
23333         }
23334     },
23335
23336     /**
23337      * Adjust the z-index of this shadow
23338      * @param {Number} zindex The new z-index
23339      */
23340     setZIndex : function(z){
23341         this.zIndex = z;
23342         if(this.el){
23343             this.el.setStyle("z-index", z);
23344         }
23345     }
23346 };
23347
23348 // Private utility class that manages the internal Shadow cache
23349 Roo.Shadow.Pool = function(){
23350     var p = [];
23351     var markup = Roo.isIE ?
23352                  '<div class="x-ie-shadow"></div>' :
23353                  '<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>';
23354     return {
23355         pull : function(){
23356             var sh = p.shift();
23357             if(!sh){
23358                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
23359                 sh.autoBoxAdjust = false;
23360             }
23361             return sh;
23362         },
23363
23364         push : function(sh){
23365             p.push(sh);
23366         }
23367     };
23368 }();/*
23369  * Based on:
23370  * Ext JS Library 1.1.1
23371  * Copyright(c) 2006-2007, Ext JS, LLC.
23372  *
23373  * Originally Released Under LGPL - original licence link has changed is not relivant.
23374  *
23375  * Fork - LGPL
23376  * <script type="text/javascript">
23377  */
23378
23379 /**
23380  * @class Roo.BoxComponent
23381  * @extends Roo.Component
23382  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
23383  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
23384  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
23385  * layout containers.
23386  * @constructor
23387  * @param {Roo.Element/String/Object} config The configuration options.
23388  */
23389 Roo.BoxComponent = function(config){
23390     Roo.Component.call(this, config);
23391     this.addEvents({
23392         /**
23393          * @event resize
23394          * Fires after the component is resized.
23395              * @param {Roo.Component} this
23396              * @param {Number} adjWidth The box-adjusted width that was set
23397              * @param {Number} adjHeight The box-adjusted height that was set
23398              * @param {Number} rawWidth The width that was originally specified
23399              * @param {Number} rawHeight The height that was originally specified
23400              */
23401         resize : true,
23402         /**
23403          * @event move
23404          * Fires after the component is moved.
23405              * @param {Roo.Component} this
23406              * @param {Number} x The new x position
23407              * @param {Number} y The new y position
23408              */
23409         move : true
23410     });
23411 };
23412
23413 Roo.extend(Roo.BoxComponent, Roo.Component, {
23414     // private, set in afterRender to signify that the component has been rendered
23415     boxReady : false,
23416     // private, used to defer height settings to subclasses
23417     deferHeight: false,
23418     /** @cfg {Number} width
23419      * width (optional) size of component
23420      */
23421      /** @cfg {Number} height
23422      * height (optional) size of component
23423      */
23424      
23425     /**
23426      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
23427      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
23428      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
23429      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
23430      * @return {Roo.BoxComponent} this
23431      */
23432     setSize : function(w, h){
23433         // support for standard size objects
23434         if(typeof w == 'object'){
23435             h = w.height;
23436             w = w.width;
23437         }
23438         // not rendered
23439         if(!this.boxReady){
23440             this.width = w;
23441             this.height = h;
23442             return this;
23443         }
23444
23445         // prevent recalcs when not needed
23446         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
23447             return this;
23448         }
23449         this.lastSize = {width: w, height: h};
23450
23451         var adj = this.adjustSize(w, h);
23452         var aw = adj.width, ah = adj.height;
23453         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
23454             var rz = this.getResizeEl();
23455             if(!this.deferHeight && aw !== undefined && ah !== undefined){
23456                 rz.setSize(aw, ah);
23457             }else if(!this.deferHeight && ah !== undefined){
23458                 rz.setHeight(ah);
23459             }else if(aw !== undefined){
23460                 rz.setWidth(aw);
23461             }
23462             this.onResize(aw, ah, w, h);
23463             this.fireEvent('resize', this, aw, ah, w, h);
23464         }
23465         return this;
23466     },
23467
23468     /**
23469      * Gets the current size of the component's underlying element.
23470      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
23471      */
23472     getSize : function(){
23473         return this.el.getSize();
23474     },
23475
23476     /**
23477      * Gets the current XY position of the component's underlying element.
23478      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23479      * @return {Array} The XY position of the element (e.g., [100, 200])
23480      */
23481     getPosition : function(local){
23482         if(local === true){
23483             return [this.el.getLeft(true), this.el.getTop(true)];
23484         }
23485         return this.xy || this.el.getXY();
23486     },
23487
23488     /**
23489      * Gets the current box measurements of the component's underlying element.
23490      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23491      * @returns {Object} box An object in the format {x, y, width, height}
23492      */
23493     getBox : function(local){
23494         var s = this.el.getSize();
23495         if(local){
23496             s.x = this.el.getLeft(true);
23497             s.y = this.el.getTop(true);
23498         }else{
23499             var xy = this.xy || this.el.getXY();
23500             s.x = xy[0];
23501             s.y = xy[1];
23502         }
23503         return s;
23504     },
23505
23506     /**
23507      * Sets the current box measurements of the component's underlying element.
23508      * @param {Object} box An object in the format {x, y, width, height}
23509      * @returns {Roo.BoxComponent} this
23510      */
23511     updateBox : function(box){
23512         this.setSize(box.width, box.height);
23513         this.setPagePosition(box.x, box.y);
23514         return this;
23515     },
23516
23517     // protected
23518     getResizeEl : function(){
23519         return this.resizeEl || this.el;
23520     },
23521
23522     // protected
23523     getPositionEl : function(){
23524         return this.positionEl || this.el;
23525     },
23526
23527     /**
23528      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23529      * This method fires the move event.
23530      * @param {Number} left The new left
23531      * @param {Number} top The new top
23532      * @returns {Roo.BoxComponent} this
23533      */
23534     setPosition : function(x, y){
23535         this.x = x;
23536         this.y = y;
23537         if(!this.boxReady){
23538             return this;
23539         }
23540         var adj = this.adjustPosition(x, y);
23541         var ax = adj.x, ay = adj.y;
23542
23543         var el = this.getPositionEl();
23544         if(ax !== undefined || ay !== undefined){
23545             if(ax !== undefined && ay !== undefined){
23546                 el.setLeftTop(ax, ay);
23547             }else if(ax !== undefined){
23548                 el.setLeft(ax);
23549             }else if(ay !== undefined){
23550                 el.setTop(ay);
23551             }
23552             this.onPosition(ax, ay);
23553             this.fireEvent('move', this, ax, ay);
23554         }
23555         return this;
23556     },
23557
23558     /**
23559      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23560      * This method fires the move event.
23561      * @param {Number} x The new x position
23562      * @param {Number} y The new y position
23563      * @returns {Roo.BoxComponent} this
23564      */
23565     setPagePosition : function(x, y){
23566         this.pageX = x;
23567         this.pageY = y;
23568         if(!this.boxReady){
23569             return;
23570         }
23571         if(x === undefined || y === undefined){ // cannot translate undefined points
23572             return;
23573         }
23574         var p = this.el.translatePoints(x, y);
23575         this.setPosition(p.left, p.top);
23576         return this;
23577     },
23578
23579     // private
23580     onRender : function(ct, position){
23581         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23582         if(this.resizeEl){
23583             this.resizeEl = Roo.get(this.resizeEl);
23584         }
23585         if(this.positionEl){
23586             this.positionEl = Roo.get(this.positionEl);
23587         }
23588     },
23589
23590     // private
23591     afterRender : function(){
23592         Roo.BoxComponent.superclass.afterRender.call(this);
23593         this.boxReady = true;
23594         this.setSize(this.width, this.height);
23595         if(this.x || this.y){
23596             this.setPosition(this.x, this.y);
23597         }
23598         if(this.pageX || this.pageY){
23599             this.setPagePosition(this.pageX, this.pageY);
23600         }
23601     },
23602
23603     /**
23604      * Force the component's size to recalculate based on the underlying element's current height and width.
23605      * @returns {Roo.BoxComponent} this
23606      */
23607     syncSize : function(){
23608         delete this.lastSize;
23609         this.setSize(this.el.getWidth(), this.el.getHeight());
23610         return this;
23611     },
23612
23613     /**
23614      * Called after the component is resized, this method is empty by default but can be implemented by any
23615      * subclass that needs to perform custom logic after a resize occurs.
23616      * @param {Number} adjWidth The box-adjusted width that was set
23617      * @param {Number} adjHeight The box-adjusted height that was set
23618      * @param {Number} rawWidth The width that was originally specified
23619      * @param {Number} rawHeight The height that was originally specified
23620      */
23621     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23622
23623     },
23624
23625     /**
23626      * Called after the component is moved, this method is empty by default but can be implemented by any
23627      * subclass that needs to perform custom logic after a move occurs.
23628      * @param {Number} x The new x position
23629      * @param {Number} y The new y position
23630      */
23631     onPosition : function(x, y){
23632
23633     },
23634
23635     // private
23636     adjustSize : function(w, h){
23637         if(this.autoWidth){
23638             w = 'auto';
23639         }
23640         if(this.autoHeight){
23641             h = 'auto';
23642         }
23643         return {width : w, height: h};
23644     },
23645
23646     // private
23647     adjustPosition : function(x, y){
23648         return {x : x, y: y};
23649     }
23650 });/*
23651  * Based on:
23652  * Ext JS Library 1.1.1
23653  * Copyright(c) 2006-2007, Ext JS, LLC.
23654  *
23655  * Originally Released Under LGPL - original licence link has changed is not relivant.
23656  *
23657  * Fork - LGPL
23658  * <script type="text/javascript">
23659  */
23660
23661
23662 /**
23663  * @class Roo.SplitBar
23664  * @extends Roo.util.Observable
23665  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23666  * <br><br>
23667  * Usage:
23668  * <pre><code>
23669 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23670                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23671 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23672 split.minSize = 100;
23673 split.maxSize = 600;
23674 split.animate = true;
23675 split.on('moved', splitterMoved);
23676 </code></pre>
23677  * @constructor
23678  * Create a new SplitBar
23679  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23680  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23681  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23682  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23683                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23684                         position of the SplitBar).
23685  */
23686 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23687     
23688     /** @private */
23689     this.el = Roo.get(dragElement, true);
23690     this.el.dom.unselectable = "on";
23691     /** @private */
23692     this.resizingEl = Roo.get(resizingElement, true);
23693
23694     /**
23695      * @private
23696      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23697      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23698      * @type Number
23699      */
23700     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23701     
23702     /**
23703      * The minimum size of the resizing element. (Defaults to 0)
23704      * @type Number
23705      */
23706     this.minSize = 0;
23707     
23708     /**
23709      * The maximum size of the resizing element. (Defaults to 2000)
23710      * @type Number
23711      */
23712     this.maxSize = 2000;
23713     
23714     /**
23715      * Whether to animate the transition to the new size
23716      * @type Boolean
23717      */
23718     this.animate = false;
23719     
23720     /**
23721      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23722      * @type Boolean
23723      */
23724     this.useShim = false;
23725     
23726     /** @private */
23727     this.shim = null;
23728     
23729     if(!existingProxy){
23730         /** @private */
23731         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23732     }else{
23733         this.proxy = Roo.get(existingProxy).dom;
23734     }
23735     /** @private */
23736     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23737     
23738     /** @private */
23739     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23740     
23741     /** @private */
23742     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23743     
23744     /** @private */
23745     this.dragSpecs = {};
23746     
23747     /**
23748      * @private The adapter to use to positon and resize elements
23749      */
23750     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23751     this.adapter.init(this);
23752     
23753     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23754         /** @private */
23755         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23756         this.el.addClass("x-splitbar-h");
23757     }else{
23758         /** @private */
23759         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23760         this.el.addClass("x-splitbar-v");
23761     }
23762     
23763     this.addEvents({
23764         /**
23765          * @event resize
23766          * Fires when the splitter is moved (alias for {@link #event-moved})
23767          * @param {Roo.SplitBar} this
23768          * @param {Number} newSize the new width or height
23769          */
23770         "resize" : true,
23771         /**
23772          * @event moved
23773          * Fires when the splitter is moved
23774          * @param {Roo.SplitBar} this
23775          * @param {Number} newSize the new width or height
23776          */
23777         "moved" : true,
23778         /**
23779          * @event beforeresize
23780          * Fires before the splitter is dragged
23781          * @param {Roo.SplitBar} this
23782          */
23783         "beforeresize" : true,
23784
23785         "beforeapply" : true
23786     });
23787
23788     Roo.util.Observable.call(this);
23789 };
23790
23791 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23792     onStartProxyDrag : function(x, y){
23793         this.fireEvent("beforeresize", this);
23794         if(!this.overlay){
23795             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23796             o.unselectable();
23797             o.enableDisplayMode("block");
23798             // all splitbars share the same overlay
23799             Roo.SplitBar.prototype.overlay = o;
23800         }
23801         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23802         this.overlay.show();
23803         Roo.get(this.proxy).setDisplayed("block");
23804         var size = this.adapter.getElementSize(this);
23805         this.activeMinSize = this.getMinimumSize();;
23806         this.activeMaxSize = this.getMaximumSize();;
23807         var c1 = size - this.activeMinSize;
23808         var c2 = Math.max(this.activeMaxSize - size, 0);
23809         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23810             this.dd.resetConstraints();
23811             this.dd.setXConstraint(
23812                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23813                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23814             );
23815             this.dd.setYConstraint(0, 0);
23816         }else{
23817             this.dd.resetConstraints();
23818             this.dd.setXConstraint(0, 0);
23819             this.dd.setYConstraint(
23820                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23821                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23822             );
23823          }
23824         this.dragSpecs.startSize = size;
23825         this.dragSpecs.startPoint = [x, y];
23826         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23827     },
23828     
23829     /** 
23830      * @private Called after the drag operation by the DDProxy
23831      */
23832     onEndProxyDrag : function(e){
23833         Roo.get(this.proxy).setDisplayed(false);
23834         var endPoint = Roo.lib.Event.getXY(e);
23835         if(this.overlay){
23836             this.overlay.hide();
23837         }
23838         var newSize;
23839         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23840             newSize = this.dragSpecs.startSize + 
23841                 (this.placement == Roo.SplitBar.LEFT ?
23842                     endPoint[0] - this.dragSpecs.startPoint[0] :
23843                     this.dragSpecs.startPoint[0] - endPoint[0]
23844                 );
23845         }else{
23846             newSize = this.dragSpecs.startSize + 
23847                 (this.placement == Roo.SplitBar.TOP ?
23848                     endPoint[1] - this.dragSpecs.startPoint[1] :
23849                     this.dragSpecs.startPoint[1] - endPoint[1]
23850                 );
23851         }
23852         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23853         if(newSize != this.dragSpecs.startSize){
23854             if(this.fireEvent('beforeapply', this, newSize) !== false){
23855                 this.adapter.setElementSize(this, newSize);
23856                 this.fireEvent("moved", this, newSize);
23857                 this.fireEvent("resize", this, newSize);
23858             }
23859         }
23860     },
23861     
23862     /**
23863      * Get the adapter this SplitBar uses
23864      * @return The adapter object
23865      */
23866     getAdapter : function(){
23867         return this.adapter;
23868     },
23869     
23870     /**
23871      * Set the adapter this SplitBar uses
23872      * @param {Object} adapter A SplitBar adapter object
23873      */
23874     setAdapter : function(adapter){
23875         this.adapter = adapter;
23876         this.adapter.init(this);
23877     },
23878     
23879     /**
23880      * Gets the minimum size for the resizing element
23881      * @return {Number} The minimum size
23882      */
23883     getMinimumSize : function(){
23884         return this.minSize;
23885     },
23886     
23887     /**
23888      * Sets the minimum size for the resizing element
23889      * @param {Number} minSize The minimum size
23890      */
23891     setMinimumSize : function(minSize){
23892         this.minSize = minSize;
23893     },
23894     
23895     /**
23896      * Gets the maximum size for the resizing element
23897      * @return {Number} The maximum size
23898      */
23899     getMaximumSize : function(){
23900         return this.maxSize;
23901     },
23902     
23903     /**
23904      * Sets the maximum size for the resizing element
23905      * @param {Number} maxSize The maximum size
23906      */
23907     setMaximumSize : function(maxSize){
23908         this.maxSize = maxSize;
23909     },
23910     
23911     /**
23912      * Sets the initialize size for the resizing element
23913      * @param {Number} size The initial size
23914      */
23915     setCurrentSize : function(size){
23916         var oldAnimate = this.animate;
23917         this.animate = false;
23918         this.adapter.setElementSize(this, size);
23919         this.animate = oldAnimate;
23920     },
23921     
23922     /**
23923      * Destroy this splitbar. 
23924      * @param {Boolean} removeEl True to remove the element
23925      */
23926     destroy : function(removeEl){
23927         if(this.shim){
23928             this.shim.remove();
23929         }
23930         this.dd.unreg();
23931         this.proxy.parentNode.removeChild(this.proxy);
23932         if(removeEl){
23933             this.el.remove();
23934         }
23935     }
23936 });
23937
23938 /**
23939  * @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.
23940  */
23941 Roo.SplitBar.createProxy = function(dir){
23942     var proxy = new Roo.Element(document.createElement("div"));
23943     proxy.unselectable();
23944     var cls = 'x-splitbar-proxy';
23945     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23946     document.body.appendChild(proxy.dom);
23947     return proxy.dom;
23948 };
23949
23950 /** 
23951  * @class Roo.SplitBar.BasicLayoutAdapter
23952  * Default Adapter. It assumes the splitter and resizing element are not positioned
23953  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23954  */
23955 Roo.SplitBar.BasicLayoutAdapter = function(){
23956 };
23957
23958 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23959     // do nothing for now
23960     init : function(s){
23961     
23962     },
23963     /**
23964      * Called before drag operations to get the current size of the resizing element. 
23965      * @param {Roo.SplitBar} s The SplitBar using this adapter
23966      */
23967      getElementSize : function(s){
23968         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23969             return s.resizingEl.getWidth();
23970         }else{
23971             return s.resizingEl.getHeight();
23972         }
23973     },
23974     
23975     /**
23976      * Called after drag operations to set the size of the resizing element.
23977      * @param {Roo.SplitBar} s The SplitBar using this adapter
23978      * @param {Number} newSize The new size to set
23979      * @param {Function} onComplete A function to be invoked when resizing is complete
23980      */
23981     setElementSize : function(s, newSize, onComplete){
23982         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23983             if(!s.animate){
23984                 s.resizingEl.setWidth(newSize);
23985                 if(onComplete){
23986                     onComplete(s, newSize);
23987                 }
23988             }else{
23989                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23990             }
23991         }else{
23992             
23993             if(!s.animate){
23994                 s.resizingEl.setHeight(newSize);
23995                 if(onComplete){
23996                     onComplete(s, newSize);
23997                 }
23998             }else{
23999                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24000             }
24001         }
24002     }
24003 };
24004
24005 /** 
24006  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24007  * @extends Roo.SplitBar.BasicLayoutAdapter
24008  * Adapter that  moves the splitter element to align with the resized sizing element. 
24009  * Used with an absolute positioned SplitBar.
24010  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24011  * document.body, make sure you assign an id to the body element.
24012  */
24013 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24014     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24015     this.container = Roo.get(container);
24016 };
24017
24018 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24019     init : function(s){
24020         this.basic.init(s);
24021     },
24022     
24023     getElementSize : function(s){
24024         return this.basic.getElementSize(s);
24025     },
24026     
24027     setElementSize : function(s, newSize, onComplete){
24028         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24029     },
24030     
24031     moveSplitter : function(s){
24032         var yes = Roo.SplitBar;
24033         switch(s.placement){
24034             case yes.LEFT:
24035                 s.el.setX(s.resizingEl.getRight());
24036                 break;
24037             case yes.RIGHT:
24038                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24039                 break;
24040             case yes.TOP:
24041                 s.el.setY(s.resizingEl.getBottom());
24042                 break;
24043             case yes.BOTTOM:
24044                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24045                 break;
24046         }
24047     }
24048 };
24049
24050 /**
24051  * Orientation constant - Create a vertical SplitBar
24052  * @static
24053  * @type Number
24054  */
24055 Roo.SplitBar.VERTICAL = 1;
24056
24057 /**
24058  * Orientation constant - Create a horizontal SplitBar
24059  * @static
24060  * @type Number
24061  */
24062 Roo.SplitBar.HORIZONTAL = 2;
24063
24064 /**
24065  * Placement constant - The resizing element is to the left of the splitter element
24066  * @static
24067  * @type Number
24068  */
24069 Roo.SplitBar.LEFT = 1;
24070
24071 /**
24072  * Placement constant - The resizing element is to the right of the splitter element
24073  * @static
24074  * @type Number
24075  */
24076 Roo.SplitBar.RIGHT = 2;
24077
24078 /**
24079  * Placement constant - The resizing element is positioned above the splitter element
24080  * @static
24081  * @type Number
24082  */
24083 Roo.SplitBar.TOP = 3;
24084
24085 /**
24086  * Placement constant - The resizing element is positioned under splitter element
24087  * @static
24088  * @type Number
24089  */
24090 Roo.SplitBar.BOTTOM = 4;
24091 /*
24092  * Based on:
24093  * Ext JS Library 1.1.1
24094  * Copyright(c) 2006-2007, Ext JS, LLC.
24095  *
24096  * Originally Released Under LGPL - original licence link has changed is not relivant.
24097  *
24098  * Fork - LGPL
24099  * <script type="text/javascript">
24100  */
24101
24102 /**
24103  * @class Roo.View
24104  * @extends Roo.util.Observable
24105  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24106  * This class also supports single and multi selection modes. <br>
24107  * Create a data model bound view:
24108  <pre><code>
24109  var store = new Roo.data.Store(...);
24110
24111  var view = new Roo.View({
24112     el : "my-element",
24113     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24114  
24115     singleSelect: true,
24116     selectedClass: "ydataview-selected",
24117     store: store
24118  });
24119
24120  // listen for node click?
24121  view.on("click", function(vw, index, node, e){
24122  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24123  });
24124
24125  // load XML data
24126  dataModel.load("foobar.xml");
24127  </code></pre>
24128  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24129  * <br><br>
24130  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24131  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24132  * 
24133  * Note: old style constructor is still suported (container, template, config)
24134  * 
24135  * @constructor
24136  * Create a new View
24137  * @param {Object} config The config object
24138  * 
24139  */
24140 Roo.View = function(config, depreciated_tpl, depreciated_config){
24141     
24142     if (typeof(depreciated_tpl) == 'undefined') {
24143         // new way.. - universal constructor.
24144         Roo.apply(this, config);
24145         this.el  = Roo.get(this.el);
24146     } else {
24147         // old format..
24148         this.el  = Roo.get(config);
24149         this.tpl = depreciated_tpl;
24150         Roo.apply(this, depreciated_config);
24151     }
24152      
24153     
24154     if(typeof(this.tpl) == "string"){
24155         this.tpl = new Roo.Template(this.tpl);
24156     } else {
24157         // support xtype ctors..
24158         this.tpl = new Roo.factory(this.tpl, Roo);
24159     }
24160     
24161     
24162     this.tpl.compile();
24163    
24164
24165      
24166     /** @private */
24167     this.addEvents({
24168         /**
24169          * @event beforeclick
24170          * Fires before a click is processed. Returns false to cancel the default action.
24171          * @param {Roo.View} this
24172          * @param {Number} index The index of the target node
24173          * @param {HTMLElement} node The target node
24174          * @param {Roo.EventObject} e The raw event object
24175          */
24176             "beforeclick" : true,
24177         /**
24178          * @event click
24179          * Fires when a template node is clicked.
24180          * @param {Roo.View} this
24181          * @param {Number} index The index of the target node
24182          * @param {HTMLElement} node The target node
24183          * @param {Roo.EventObject} e The raw event object
24184          */
24185             "click" : true,
24186         /**
24187          * @event dblclick
24188          * Fires when a template node is double clicked.
24189          * @param {Roo.View} this
24190          * @param {Number} index The index of the target node
24191          * @param {HTMLElement} node The target node
24192          * @param {Roo.EventObject} e The raw event object
24193          */
24194             "dblclick" : true,
24195         /**
24196          * @event contextmenu
24197          * Fires when a template node is right clicked.
24198          * @param {Roo.View} this
24199          * @param {Number} index The index of the target node
24200          * @param {HTMLElement} node The target node
24201          * @param {Roo.EventObject} e The raw event object
24202          */
24203             "contextmenu" : true,
24204         /**
24205          * @event selectionchange
24206          * Fires when the selected nodes change.
24207          * @param {Roo.View} this
24208          * @param {Array} selections Array of the selected nodes
24209          */
24210             "selectionchange" : true,
24211     
24212         /**
24213          * @event beforeselect
24214          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24215          * @param {Roo.View} this
24216          * @param {HTMLElement} node The node to be selected
24217          * @param {Array} selections Array of currently selected nodes
24218          */
24219             "beforeselect" : true,
24220         /**
24221          * @event preparedata
24222          * Fires on every row to render, to allow you to change the data.
24223          * @param {Roo.View} this
24224          * @param {Object} data to be rendered (change this)
24225          */
24226           "preparedata" : true
24227         });
24228
24229     this.el.on({
24230         "click": this.onClick,
24231         "dblclick": this.onDblClick,
24232         "contextmenu": this.onContextMenu,
24233         scope:this
24234     });
24235
24236     this.selections = [];
24237     this.nodes = [];
24238     this.cmp = new Roo.CompositeElementLite([]);
24239     if(this.store){
24240         this.store = Roo.factory(this.store, Roo.data);
24241         this.setStore(this.store, true);
24242     }
24243     Roo.View.superclass.constructor.call(this);
24244 };
24245
24246 Roo.extend(Roo.View, Roo.util.Observable, {
24247     
24248      /**
24249      * @cfg {Roo.data.Store} store Data store to load data from.
24250      */
24251     store : false,
24252     
24253     /**
24254      * @cfg {String|Roo.Element} el The container element.
24255      */
24256     el : '',
24257     
24258     /**
24259      * @cfg {String|Roo.Template} tpl The template used by this View 
24260      */
24261     tpl : false,
24262     /**
24263      * @cfg {String} dataName the named area of the template to use as the data area
24264      *                          Works with domtemplates roo-name="name"
24265      */
24266     dataName: false,
24267     /**
24268      * @cfg {String} selectedClass The css class to add to selected nodes
24269      */
24270     selectedClass : "x-view-selected",
24271      /**
24272      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24273      */
24274     emptyText : "",
24275     
24276     /**
24277      * @cfg {String} text to display on mask (default Loading)
24278      */
24279     mask : false,
24280     /**
24281      * @cfg {Boolean} multiSelect Allow multiple selection
24282      */
24283     multiSelect : false,
24284     /**
24285      * @cfg {Boolean} singleSelect Allow single selection
24286      */
24287     singleSelect:  false,
24288     
24289     /**
24290      * @cfg {Boolean} toggleSelect - selecting 
24291      */
24292     toggleSelect : false,
24293     
24294     /**
24295      * Returns the element this view is bound to.
24296      * @return {Roo.Element}
24297      */
24298     getEl : function(){
24299         return this.el;
24300     },
24301
24302     /**
24303      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24304      */
24305     refresh : function(){
24306         var t = this.tpl;
24307         
24308         // if we are using something like 'domtemplate', then
24309         // the what gets used is:
24310         // t.applySubtemplate(NAME, data, wrapping data..)
24311         // the outer template then get' applied with
24312         //     the store 'extra data'
24313         // and the body get's added to the
24314         //      roo-name="data" node?
24315         //      <span class='roo-tpl-{name}'></span> ?????
24316         
24317         
24318         
24319         this.clearSelections();
24320         this.el.update("");
24321         var html = [];
24322         var records = this.store.getRange();
24323         if(records.length < 1) {
24324             
24325             // is this valid??  = should it render a template??
24326             
24327             this.el.update(this.emptyText);
24328             return;
24329         }
24330         var el = this.el;
24331         if (this.dataName) {
24332             this.el.update(t.apply(this.store.meta)); //????
24333             el = this.el.child('.roo-tpl-' + this.dataName);
24334         }
24335         
24336         for(var i = 0, len = records.length; i < len; i++){
24337             var data = this.prepareData(records[i].data, i, records[i]);
24338             this.fireEvent("preparedata", this, data, i, records[i]);
24339             html[html.length] = Roo.util.Format.trim(
24340                 this.dataName ?
24341                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24342                     t.apply(data)
24343             );
24344         }
24345         
24346         
24347         
24348         el.update(html.join(""));
24349         this.nodes = el.dom.childNodes;
24350         this.updateIndexes(0);
24351     },
24352
24353     /**
24354      * Function to override to reformat the data that is sent to
24355      * the template for each node.
24356      * DEPRICATED - use the preparedata event handler.
24357      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24358      * a JSON object for an UpdateManager bound view).
24359      */
24360     prepareData : function(data, index, record)
24361     {
24362         this.fireEvent("preparedata", this, data, index, record);
24363         return data;
24364     },
24365
24366     onUpdate : function(ds, record){
24367         this.clearSelections();
24368         var index = this.store.indexOf(record);
24369         var n = this.nodes[index];
24370         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24371         n.parentNode.removeChild(n);
24372         this.updateIndexes(index, index);
24373     },
24374
24375     
24376     
24377 // --------- FIXME     
24378     onAdd : function(ds, records, index)
24379     {
24380         this.clearSelections();
24381         if(this.nodes.length == 0){
24382             this.refresh();
24383             return;
24384         }
24385         var n = this.nodes[index];
24386         for(var i = 0, len = records.length; i < len; i++){
24387             var d = this.prepareData(records[i].data, i, records[i]);
24388             if(n){
24389                 this.tpl.insertBefore(n, d);
24390             }else{
24391                 
24392                 this.tpl.append(this.el, d);
24393             }
24394         }
24395         this.updateIndexes(index);
24396     },
24397
24398     onRemove : function(ds, record, index){
24399         this.clearSelections();
24400         var el = this.dataName  ?
24401             this.el.child('.roo-tpl-' + this.dataName) :
24402             this.el; 
24403         el.dom.removeChild(this.nodes[index]);
24404         this.updateIndexes(index);
24405     },
24406
24407     /**
24408      * Refresh an individual node.
24409      * @param {Number} index
24410      */
24411     refreshNode : function(index){
24412         this.onUpdate(this.store, this.store.getAt(index));
24413     },
24414
24415     updateIndexes : function(startIndex, endIndex){
24416         var ns = this.nodes;
24417         startIndex = startIndex || 0;
24418         endIndex = endIndex || ns.length - 1;
24419         for(var i = startIndex; i <= endIndex; i++){
24420             ns[i].nodeIndex = i;
24421         }
24422     },
24423
24424     /**
24425      * Changes the data store this view uses and refresh the view.
24426      * @param {Store} store
24427      */
24428     setStore : function(store, initial){
24429         if(!initial && this.store){
24430             this.store.un("datachanged", this.refresh);
24431             this.store.un("add", this.onAdd);
24432             this.store.un("remove", this.onRemove);
24433             this.store.un("update", this.onUpdate);
24434             this.store.un("clear", this.refresh);
24435             this.store.un("beforeload", this.onBeforeLoad);
24436             this.store.un("load", this.onLoad);
24437             this.store.un("loadexception", this.onLoad);
24438         }
24439         if(store){
24440           
24441             store.on("datachanged", this.refresh, this);
24442             store.on("add", this.onAdd, this);
24443             store.on("remove", this.onRemove, this);
24444             store.on("update", this.onUpdate, this);
24445             store.on("clear", this.refresh, this);
24446             store.on("beforeload", this.onBeforeLoad, this);
24447             store.on("load", this.onLoad, this);
24448             store.on("loadexception", this.onLoad, this);
24449         }
24450         
24451         if(store){
24452             this.refresh();
24453         }
24454     },
24455     /**
24456      * onbeforeLoad - masks the loading area.
24457      *
24458      */
24459     onBeforeLoad : function()
24460     {
24461         this.el.update("");
24462         this.el.mask(this.mask ? this.mask : "Loading" ); 
24463     },
24464     onLoad : function ()
24465     {
24466         this.el.unmask();
24467     },
24468     
24469
24470     /**
24471      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
24472      * @param {HTMLElement} node
24473      * @return {HTMLElement} The template node
24474      */
24475     findItemFromChild : function(node){
24476         var el = this.dataName  ?
24477             this.el.child('.roo-tpl-' + this.dataName,true) :
24478             this.el.dom; 
24479         
24480         if(!node || node.parentNode == el){
24481                     return node;
24482             }
24483             var p = node.parentNode;
24484             while(p && p != el){
24485             if(p.parentNode == el){
24486                 return p;
24487             }
24488             p = p.parentNode;
24489         }
24490             return null;
24491     },
24492
24493     /** @ignore */
24494     onClick : function(e){
24495         var item = this.findItemFromChild(e.getTarget());
24496         if(item){
24497             var index = this.indexOf(item);
24498             if(this.onItemClick(item, index, e) !== false){
24499                 this.fireEvent("click", this, index, item, e);
24500             }
24501         }else{
24502             this.clearSelections();
24503         }
24504     },
24505
24506     /** @ignore */
24507     onContextMenu : function(e){
24508         var item = this.findItemFromChild(e.getTarget());
24509         if(item){
24510             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
24511         }
24512     },
24513
24514     /** @ignore */
24515     onDblClick : function(e){
24516         var item = this.findItemFromChild(e.getTarget());
24517         if(item){
24518             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
24519         }
24520     },
24521
24522     onItemClick : function(item, index, e)
24523     {
24524         if(this.fireEvent("beforeclick", this, index, item, e) === false){
24525             return false;
24526         }
24527         if (this.toggleSelect) {
24528             var m = this.isSelected(item) ? 'unselect' : 'select';
24529             Roo.log(m);
24530             var _t = this;
24531             _t[m](item, true, false);
24532             return true;
24533         }
24534         if(this.multiSelect || this.singleSelect){
24535             if(this.multiSelect && e.shiftKey && this.lastSelection){
24536                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
24537             }else{
24538                 this.select(item, this.multiSelect && e.ctrlKey);
24539                 this.lastSelection = item;
24540             }
24541             e.preventDefault();
24542         }
24543         return true;
24544     },
24545
24546     /**
24547      * Get the number of selected nodes.
24548      * @return {Number}
24549      */
24550     getSelectionCount : function(){
24551         return this.selections.length;
24552     },
24553
24554     /**
24555      * Get the currently selected nodes.
24556      * @return {Array} An array of HTMLElements
24557      */
24558     getSelectedNodes : function(){
24559         return this.selections;
24560     },
24561
24562     /**
24563      * Get the indexes of the selected nodes.
24564      * @return {Array}
24565      */
24566     getSelectedIndexes : function(){
24567         var indexes = [], s = this.selections;
24568         for(var i = 0, len = s.length; i < len; i++){
24569             indexes.push(s[i].nodeIndex);
24570         }
24571         return indexes;
24572     },
24573
24574     /**
24575      * Clear all selections
24576      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
24577      */
24578     clearSelections : function(suppressEvent){
24579         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24580             this.cmp.elements = this.selections;
24581             this.cmp.removeClass(this.selectedClass);
24582             this.selections = [];
24583             if(!suppressEvent){
24584                 this.fireEvent("selectionchange", this, this.selections);
24585             }
24586         }
24587     },
24588
24589     /**
24590      * Returns true if the passed node is selected
24591      * @param {HTMLElement/Number} node The node or node index
24592      * @return {Boolean}
24593      */
24594     isSelected : function(node){
24595         var s = this.selections;
24596         if(s.length < 1){
24597             return false;
24598         }
24599         node = this.getNode(node);
24600         return s.indexOf(node) !== -1;
24601     },
24602
24603     /**
24604      * Selects nodes.
24605      * @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
24606      * @param {Boolean} keepExisting (optional) true to keep existing selections
24607      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24608      */
24609     select : function(nodeInfo, keepExisting, suppressEvent){
24610         if(nodeInfo instanceof Array){
24611             if(!keepExisting){
24612                 this.clearSelections(true);
24613             }
24614             for(var i = 0, len = nodeInfo.length; i < len; i++){
24615                 this.select(nodeInfo[i], true, true);
24616             }
24617             return;
24618         } 
24619         var node = this.getNode(nodeInfo);
24620         if(!node || this.isSelected(node)){
24621             return; // already selected.
24622         }
24623         if(!keepExisting){
24624             this.clearSelections(true);
24625         }
24626         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24627             Roo.fly(node).addClass(this.selectedClass);
24628             this.selections.push(node);
24629             if(!suppressEvent){
24630                 this.fireEvent("selectionchange", this, this.selections);
24631             }
24632         }
24633         
24634         
24635     },
24636       /**
24637      * Unselects nodes.
24638      * @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
24639      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24640      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24641      */
24642     unselect : function(nodeInfo, keepExisting, suppressEvent)
24643     {
24644         if(nodeInfo instanceof Array){
24645             Roo.each(this.selections, function(s) {
24646                 this.unselect(s, nodeInfo);
24647             }, this);
24648             return;
24649         }
24650         var node = this.getNode(nodeInfo);
24651         if(!node || !this.isSelected(node)){
24652             Roo.log("not selected");
24653             return; // not selected.
24654         }
24655         // fireevent???
24656         var ns = [];
24657         Roo.each(this.selections, function(s) {
24658             if (s == node ) {
24659                 Roo.fly(node).removeClass(this.selectedClass);
24660
24661                 return;
24662             }
24663             ns.push(s);
24664         },this);
24665         
24666         this.selections= ns;
24667         this.fireEvent("selectionchange", this, this.selections);
24668     },
24669
24670     /**
24671      * Gets a template node.
24672      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24673      * @return {HTMLElement} The node or null if it wasn't found
24674      */
24675     getNode : function(nodeInfo){
24676         if(typeof nodeInfo == "string"){
24677             return document.getElementById(nodeInfo);
24678         }else if(typeof nodeInfo == "number"){
24679             return this.nodes[nodeInfo];
24680         }
24681         return nodeInfo;
24682     },
24683
24684     /**
24685      * Gets a range template nodes.
24686      * @param {Number} startIndex
24687      * @param {Number} endIndex
24688      * @return {Array} An array of nodes
24689      */
24690     getNodes : function(start, end){
24691         var ns = this.nodes;
24692         start = start || 0;
24693         end = typeof end == "undefined" ? ns.length - 1 : end;
24694         var nodes = [];
24695         if(start <= end){
24696             for(var i = start; i <= end; i++){
24697                 nodes.push(ns[i]);
24698             }
24699         } else{
24700             for(var i = start; i >= end; i--){
24701                 nodes.push(ns[i]);
24702             }
24703         }
24704         return nodes;
24705     },
24706
24707     /**
24708      * Finds the index of the passed node
24709      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24710      * @return {Number} The index of the node or -1
24711      */
24712     indexOf : function(node){
24713         node = this.getNode(node);
24714         if(typeof node.nodeIndex == "number"){
24715             return node.nodeIndex;
24716         }
24717         var ns = this.nodes;
24718         for(var i = 0, len = ns.length; i < len; i++){
24719             if(ns[i] == node){
24720                 return i;
24721             }
24722         }
24723         return -1;
24724     }
24725 });
24726 /*
24727  * Based on:
24728  * Ext JS Library 1.1.1
24729  * Copyright(c) 2006-2007, Ext JS, LLC.
24730  *
24731  * Originally Released Under LGPL - original licence link has changed is not relivant.
24732  *
24733  * Fork - LGPL
24734  * <script type="text/javascript">
24735  */
24736
24737 /**
24738  * @class Roo.JsonView
24739  * @extends Roo.View
24740  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24741 <pre><code>
24742 var view = new Roo.JsonView({
24743     container: "my-element",
24744     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24745     multiSelect: true, 
24746     jsonRoot: "data" 
24747 });
24748
24749 // listen for node click?
24750 view.on("click", function(vw, index, node, e){
24751     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24752 });
24753
24754 // direct load of JSON data
24755 view.load("foobar.php");
24756
24757 // Example from my blog list
24758 var tpl = new Roo.Template(
24759     '&lt;div class="entry"&gt;' +
24760     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24761     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24762     "&lt;/div&gt;&lt;hr /&gt;"
24763 );
24764
24765 var moreView = new Roo.JsonView({
24766     container :  "entry-list", 
24767     template : tpl,
24768     jsonRoot: "posts"
24769 });
24770 moreView.on("beforerender", this.sortEntries, this);
24771 moreView.load({
24772     url: "/blog/get-posts.php",
24773     params: "allposts=true",
24774     text: "Loading Blog Entries..."
24775 });
24776 </code></pre>
24777
24778 * Note: old code is supported with arguments : (container, template, config)
24779
24780
24781  * @constructor
24782  * Create a new JsonView
24783  * 
24784  * @param {Object} config The config object
24785  * 
24786  */
24787 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24788     
24789     
24790     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24791
24792     var um = this.el.getUpdateManager();
24793     um.setRenderer(this);
24794     um.on("update", this.onLoad, this);
24795     um.on("failure", this.onLoadException, this);
24796
24797     /**
24798      * @event beforerender
24799      * Fires before rendering of the downloaded JSON data.
24800      * @param {Roo.JsonView} this
24801      * @param {Object} data The JSON data loaded
24802      */
24803     /**
24804      * @event load
24805      * Fires when data is loaded.
24806      * @param {Roo.JsonView} this
24807      * @param {Object} data The JSON data loaded
24808      * @param {Object} response The raw Connect response object
24809      */
24810     /**
24811      * @event loadexception
24812      * Fires when loading fails.
24813      * @param {Roo.JsonView} this
24814      * @param {Object} response The raw Connect response object
24815      */
24816     this.addEvents({
24817         'beforerender' : true,
24818         'load' : true,
24819         'loadexception' : true
24820     });
24821 };
24822 Roo.extend(Roo.JsonView, Roo.View, {
24823     /**
24824      * @type {String} The root property in the loaded JSON object that contains the data
24825      */
24826     jsonRoot : "",
24827
24828     /**
24829      * Refreshes the view.
24830      */
24831     refresh : function(){
24832         this.clearSelections();
24833         this.el.update("");
24834         var html = [];
24835         var o = this.jsonData;
24836         if(o && o.length > 0){
24837             for(var i = 0, len = o.length; i < len; i++){
24838                 var data = this.prepareData(o[i], i, o);
24839                 html[html.length] = this.tpl.apply(data);
24840             }
24841         }else{
24842             html.push(this.emptyText);
24843         }
24844         this.el.update(html.join(""));
24845         this.nodes = this.el.dom.childNodes;
24846         this.updateIndexes(0);
24847     },
24848
24849     /**
24850      * 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.
24851      * @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:
24852      <pre><code>
24853      view.load({
24854          url: "your-url.php",
24855          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24856          callback: yourFunction,
24857          scope: yourObject, //(optional scope)
24858          discardUrl: false,
24859          nocache: false,
24860          text: "Loading...",
24861          timeout: 30,
24862          scripts: false
24863      });
24864      </code></pre>
24865      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24866      * 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.
24867      * @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}
24868      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24869      * @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.
24870      */
24871     load : function(){
24872         var um = this.el.getUpdateManager();
24873         um.update.apply(um, arguments);
24874     },
24875
24876     render : function(el, response){
24877         this.clearSelections();
24878         this.el.update("");
24879         var o;
24880         try{
24881             o = Roo.util.JSON.decode(response.responseText);
24882             if(this.jsonRoot){
24883                 
24884                 o = o[this.jsonRoot];
24885             }
24886         } catch(e){
24887         }
24888         /**
24889          * The current JSON data or null
24890          */
24891         this.jsonData = o;
24892         this.beforeRender();
24893         this.refresh();
24894     },
24895
24896 /**
24897  * Get the number of records in the current JSON dataset
24898  * @return {Number}
24899  */
24900     getCount : function(){
24901         return this.jsonData ? this.jsonData.length : 0;
24902     },
24903
24904 /**
24905  * Returns the JSON object for the specified node(s)
24906  * @param {HTMLElement/Array} node The node or an array of nodes
24907  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24908  * you get the JSON object for the node
24909  */
24910     getNodeData : function(node){
24911         if(node instanceof Array){
24912             var data = [];
24913             for(var i = 0, len = node.length; i < len; i++){
24914                 data.push(this.getNodeData(node[i]));
24915             }
24916             return data;
24917         }
24918         return this.jsonData[this.indexOf(node)] || null;
24919     },
24920
24921     beforeRender : function(){
24922         this.snapshot = this.jsonData;
24923         if(this.sortInfo){
24924             this.sort.apply(this, this.sortInfo);
24925         }
24926         this.fireEvent("beforerender", this, this.jsonData);
24927     },
24928
24929     onLoad : function(el, o){
24930         this.fireEvent("load", this, this.jsonData, o);
24931     },
24932
24933     onLoadException : function(el, o){
24934         this.fireEvent("loadexception", this, o);
24935     },
24936
24937 /**
24938  * Filter the data by a specific property.
24939  * @param {String} property A property on your JSON objects
24940  * @param {String/RegExp} value Either string that the property values
24941  * should start with, or a RegExp to test against the property
24942  */
24943     filter : function(property, value){
24944         if(this.jsonData){
24945             var data = [];
24946             var ss = this.snapshot;
24947             if(typeof value == "string"){
24948                 var vlen = value.length;
24949                 if(vlen == 0){
24950                     this.clearFilter();
24951                     return;
24952                 }
24953                 value = value.toLowerCase();
24954                 for(var i = 0, len = ss.length; i < len; i++){
24955                     var o = ss[i];
24956                     if(o[property].substr(0, vlen).toLowerCase() == value){
24957                         data.push(o);
24958                     }
24959                 }
24960             } else if(value.exec){ // regex?
24961                 for(var i = 0, len = ss.length; i < len; i++){
24962                     var o = ss[i];
24963                     if(value.test(o[property])){
24964                         data.push(o);
24965                     }
24966                 }
24967             } else{
24968                 return;
24969             }
24970             this.jsonData = data;
24971             this.refresh();
24972         }
24973     },
24974
24975 /**
24976  * Filter by a function. The passed function will be called with each
24977  * object in the current dataset. If the function returns true the value is kept,
24978  * otherwise it is filtered.
24979  * @param {Function} fn
24980  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24981  */
24982     filterBy : function(fn, scope){
24983         if(this.jsonData){
24984             var data = [];
24985             var ss = this.snapshot;
24986             for(var i = 0, len = ss.length; i < len; i++){
24987                 var o = ss[i];
24988                 if(fn.call(scope || this, o)){
24989                     data.push(o);
24990                 }
24991             }
24992             this.jsonData = data;
24993             this.refresh();
24994         }
24995     },
24996
24997 /**
24998  * Clears the current filter.
24999  */
25000     clearFilter : function(){
25001         if(this.snapshot && this.jsonData != this.snapshot){
25002             this.jsonData = this.snapshot;
25003             this.refresh();
25004         }
25005     },
25006
25007
25008 /**
25009  * Sorts the data for this view and refreshes it.
25010  * @param {String} property A property on your JSON objects to sort on
25011  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25012  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25013  */
25014     sort : function(property, dir, sortType){
25015         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25016         if(this.jsonData){
25017             var p = property;
25018             var dsc = dir && dir.toLowerCase() == "desc";
25019             var f = function(o1, o2){
25020                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25021                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25022                 ;
25023                 if(v1 < v2){
25024                     return dsc ? +1 : -1;
25025                 } else if(v1 > v2){
25026                     return dsc ? -1 : +1;
25027                 } else{
25028                     return 0;
25029                 }
25030             };
25031             this.jsonData.sort(f);
25032             this.refresh();
25033             if(this.jsonData != this.snapshot){
25034                 this.snapshot.sort(f);
25035             }
25036         }
25037     }
25038 });/*
25039  * Based on:
25040  * Ext JS Library 1.1.1
25041  * Copyright(c) 2006-2007, Ext JS, LLC.
25042  *
25043  * Originally Released Under LGPL - original licence link has changed is not relivant.
25044  *
25045  * Fork - LGPL
25046  * <script type="text/javascript">
25047  */
25048  
25049
25050 /**
25051  * @class Roo.ColorPalette
25052  * @extends Roo.Component
25053  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25054  * Here's an example of typical usage:
25055  * <pre><code>
25056 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25057 cp.render('my-div');
25058
25059 cp.on('select', function(palette, selColor){
25060     // do something with selColor
25061 });
25062 </code></pre>
25063  * @constructor
25064  * Create a new ColorPalette
25065  * @param {Object} config The config object
25066  */
25067 Roo.ColorPalette = function(config){
25068     Roo.ColorPalette.superclass.constructor.call(this, config);
25069     this.addEvents({
25070         /**
25071              * @event select
25072              * Fires when a color is selected
25073              * @param {ColorPalette} this
25074              * @param {String} color The 6-digit color hex code (without the # symbol)
25075              */
25076         select: true
25077     });
25078
25079     if(this.handler){
25080         this.on("select", this.handler, this.scope, true);
25081     }
25082 };
25083 Roo.extend(Roo.ColorPalette, Roo.Component, {
25084     /**
25085      * @cfg {String} itemCls
25086      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25087      */
25088     itemCls : "x-color-palette",
25089     /**
25090      * @cfg {String} value
25091      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25092      * the hex codes are case-sensitive.
25093      */
25094     value : null,
25095     clickEvent:'click',
25096     // private
25097     ctype: "Roo.ColorPalette",
25098
25099     /**
25100      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25101      */
25102     allowReselect : false,
25103
25104     /**
25105      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25106      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25107      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25108      * of colors with the width setting until the box is symmetrical.</p>
25109      * <p>You can override individual colors if needed:</p>
25110      * <pre><code>
25111 var cp = new Roo.ColorPalette();
25112 cp.colors[0] = "FF0000";  // change the first box to red
25113 </code></pre>
25114
25115 Or you can provide a custom array of your own for complete control:
25116 <pre><code>
25117 var cp = new Roo.ColorPalette();
25118 cp.colors = ["000000", "993300", "333300"];
25119 </code></pre>
25120      * @type Array
25121      */
25122     colors : [
25123         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25124         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25125         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25126         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25127         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25128     ],
25129
25130     // private
25131     onRender : function(container, position){
25132         var t = new Roo.MasterTemplate(
25133             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25134         );
25135         var c = this.colors;
25136         for(var i = 0, len = c.length; i < len; i++){
25137             t.add([c[i]]);
25138         }
25139         var el = document.createElement("div");
25140         el.className = this.itemCls;
25141         t.overwrite(el);
25142         container.dom.insertBefore(el, position);
25143         this.el = Roo.get(el);
25144         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25145         if(this.clickEvent != 'click'){
25146             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25147         }
25148     },
25149
25150     // private
25151     afterRender : function(){
25152         Roo.ColorPalette.superclass.afterRender.call(this);
25153         if(this.value){
25154             var s = this.value;
25155             this.value = null;
25156             this.select(s);
25157         }
25158     },
25159
25160     // private
25161     handleClick : function(e, t){
25162         e.preventDefault();
25163         if(!this.disabled){
25164             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25165             this.select(c.toUpperCase());
25166         }
25167     },
25168
25169     /**
25170      * Selects the specified color in the palette (fires the select event)
25171      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25172      */
25173     select : function(color){
25174         color = color.replace("#", "");
25175         if(color != this.value || this.allowReselect){
25176             var el = this.el;
25177             if(this.value){
25178                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25179             }
25180             el.child("a.color-"+color).addClass("x-color-palette-sel");
25181             this.value = color;
25182             this.fireEvent("select", this, color);
25183         }
25184     }
25185 });/*
25186  * Based on:
25187  * Ext JS Library 1.1.1
25188  * Copyright(c) 2006-2007, Ext JS, LLC.
25189  *
25190  * Originally Released Under LGPL - original licence link has changed is not relivant.
25191  *
25192  * Fork - LGPL
25193  * <script type="text/javascript">
25194  */
25195  
25196 /**
25197  * @class Roo.DatePicker
25198  * @extends Roo.Component
25199  * Simple date picker class.
25200  * @constructor
25201  * Create a new DatePicker
25202  * @param {Object} config The config object
25203  */
25204 Roo.DatePicker = function(config){
25205     Roo.DatePicker.superclass.constructor.call(this, config);
25206
25207     this.value = config && config.value ?
25208                  config.value.clearTime() : new Date().clearTime();
25209
25210     this.addEvents({
25211         /**
25212              * @event select
25213              * Fires when a date is selected
25214              * @param {DatePicker} this
25215              * @param {Date} date The selected date
25216              */
25217         'select': true,
25218         /**
25219              * @event monthchange
25220              * Fires when the displayed month changes 
25221              * @param {DatePicker} this
25222              * @param {Date} date The selected month
25223              */
25224         'monthchange': true
25225     });
25226
25227     if(this.handler){
25228         this.on("select", this.handler,  this.scope || this);
25229     }
25230     // build the disabledDatesRE
25231     if(!this.disabledDatesRE && this.disabledDates){
25232         var dd = this.disabledDates;
25233         var re = "(?:";
25234         for(var i = 0; i < dd.length; i++){
25235             re += dd[i];
25236             if(i != dd.length-1) re += "|";
25237         }
25238         this.disabledDatesRE = new RegExp(re + ")");
25239     }
25240 };
25241
25242 Roo.extend(Roo.DatePicker, Roo.Component, {
25243     /**
25244      * @cfg {String} todayText
25245      * The text to display on the button that selects the current date (defaults to "Today")
25246      */
25247     todayText : "Today",
25248     /**
25249      * @cfg {String} okText
25250      * The text to display on the ok button
25251      */
25252     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25253     /**
25254      * @cfg {String} cancelText
25255      * The text to display on the cancel button
25256      */
25257     cancelText : "Cancel",
25258     /**
25259      * @cfg {String} todayTip
25260      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25261      */
25262     todayTip : "{0} (Spacebar)",
25263     /**
25264      * @cfg {Date} minDate
25265      * Minimum allowable date (JavaScript date object, defaults to null)
25266      */
25267     minDate : null,
25268     /**
25269      * @cfg {Date} maxDate
25270      * Maximum allowable date (JavaScript date object, defaults to null)
25271      */
25272     maxDate : null,
25273     /**
25274      * @cfg {String} minText
25275      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25276      */
25277     minText : "This date is before the minimum date",
25278     /**
25279      * @cfg {String} maxText
25280      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25281      */
25282     maxText : "This date is after the maximum date",
25283     /**
25284      * @cfg {String} format
25285      * The default date format string which can be overriden for localization support.  The format must be
25286      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25287      */
25288     format : "m/d/y",
25289     /**
25290      * @cfg {Array} disabledDays
25291      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25292      */
25293     disabledDays : null,
25294     /**
25295      * @cfg {String} disabledDaysText
25296      * The tooltip to display when the date falls on a disabled day (defaults to "")
25297      */
25298     disabledDaysText : "",
25299     /**
25300      * @cfg {RegExp} disabledDatesRE
25301      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25302      */
25303     disabledDatesRE : null,
25304     /**
25305      * @cfg {String} disabledDatesText
25306      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25307      */
25308     disabledDatesText : "",
25309     /**
25310      * @cfg {Boolean} constrainToViewport
25311      * True to constrain the date picker to the viewport (defaults to true)
25312      */
25313     constrainToViewport : true,
25314     /**
25315      * @cfg {Array} monthNames
25316      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25317      */
25318     monthNames : Date.monthNames,
25319     /**
25320      * @cfg {Array} dayNames
25321      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25322      */
25323     dayNames : Date.dayNames,
25324     /**
25325      * @cfg {String} nextText
25326      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25327      */
25328     nextText: 'Next Month (Control+Right)',
25329     /**
25330      * @cfg {String} prevText
25331      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25332      */
25333     prevText: 'Previous Month (Control+Left)',
25334     /**
25335      * @cfg {String} monthYearText
25336      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25337      */
25338     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25339     /**
25340      * @cfg {Number} startDay
25341      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25342      */
25343     startDay : 0,
25344     /**
25345      * @cfg {Bool} showClear
25346      * Show a clear button (usefull for date form elements that can be blank.)
25347      */
25348     
25349     showClear: false,
25350     
25351     /**
25352      * Sets the value of the date field
25353      * @param {Date} value The date to set
25354      */
25355     setValue : function(value){
25356         var old = this.value;
25357         
25358         if (typeof(value) == 'string') {
25359          
25360             value = Date.parseDate(value, this.format);
25361         }
25362         if (!value) {
25363             value = new Date();
25364         }
25365         
25366         this.value = value.clearTime(true);
25367         if(this.el){
25368             this.update(this.value);
25369         }
25370     },
25371
25372     /**
25373      * Gets the current selected value of the date field
25374      * @return {Date} The selected date
25375      */
25376     getValue : function(){
25377         return this.value;
25378     },
25379
25380     // private
25381     focus : function(){
25382         if(this.el){
25383             this.update(this.activeDate);
25384         }
25385     },
25386
25387     // privateval
25388     onRender : function(container, position){
25389         
25390         var m = [
25391              '<table cellspacing="0">',
25392                 '<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>',
25393                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
25394         var dn = this.dayNames;
25395         for(var i = 0; i < 7; i++){
25396             var d = this.startDay+i;
25397             if(d > 6){
25398                 d = d-7;
25399             }
25400             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
25401         }
25402         m[m.length] = "</tr></thead><tbody><tr>";
25403         for(var i = 0; i < 42; i++) {
25404             if(i % 7 == 0 && i != 0){
25405                 m[m.length] = "</tr><tr>";
25406             }
25407             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
25408         }
25409         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
25410             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
25411
25412         var el = document.createElement("div");
25413         el.className = "x-date-picker";
25414         el.innerHTML = m.join("");
25415
25416         container.dom.insertBefore(el, position);
25417
25418         this.el = Roo.get(el);
25419         this.eventEl = Roo.get(el.firstChild);
25420
25421         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
25422             handler: this.showPrevMonth,
25423             scope: this,
25424             preventDefault:true,
25425             stopDefault:true
25426         });
25427
25428         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
25429             handler: this.showNextMonth,
25430             scope: this,
25431             preventDefault:true,
25432             stopDefault:true
25433         });
25434
25435         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
25436
25437         this.monthPicker = this.el.down('div.x-date-mp');
25438         this.monthPicker.enableDisplayMode('block');
25439         
25440         var kn = new Roo.KeyNav(this.eventEl, {
25441             "left" : function(e){
25442                 e.ctrlKey ?
25443                     this.showPrevMonth() :
25444                     this.update(this.activeDate.add("d", -1));
25445             },
25446
25447             "right" : function(e){
25448                 e.ctrlKey ?
25449                     this.showNextMonth() :
25450                     this.update(this.activeDate.add("d", 1));
25451             },
25452
25453             "up" : function(e){
25454                 e.ctrlKey ?
25455                     this.showNextYear() :
25456                     this.update(this.activeDate.add("d", -7));
25457             },
25458
25459             "down" : function(e){
25460                 e.ctrlKey ?
25461                     this.showPrevYear() :
25462                     this.update(this.activeDate.add("d", 7));
25463             },
25464
25465             "pageUp" : function(e){
25466                 this.showNextMonth();
25467             },
25468
25469             "pageDown" : function(e){
25470                 this.showPrevMonth();
25471             },
25472
25473             "enter" : function(e){
25474                 e.stopPropagation();
25475                 return true;
25476             },
25477
25478             scope : this
25479         });
25480
25481         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
25482
25483         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
25484
25485         this.el.unselectable();
25486         
25487         this.cells = this.el.select("table.x-date-inner tbody td");
25488         this.textNodes = this.el.query("table.x-date-inner tbody span");
25489
25490         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
25491             text: "&#160;",
25492             tooltip: this.monthYearText
25493         });
25494
25495         this.mbtn.on('click', this.showMonthPicker, this);
25496         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
25497
25498
25499         var today = (new Date()).dateFormat(this.format);
25500         
25501         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
25502         if (this.showClear) {
25503             baseTb.add( new Roo.Toolbar.Fill());
25504         }
25505         baseTb.add({
25506             text: String.format(this.todayText, today),
25507             tooltip: String.format(this.todayTip, today),
25508             handler: this.selectToday,
25509             scope: this
25510         });
25511         
25512         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
25513             
25514         //});
25515         if (this.showClear) {
25516             
25517             baseTb.add( new Roo.Toolbar.Fill());
25518             baseTb.add({
25519                 text: '&#160;',
25520                 cls: 'x-btn-icon x-btn-clear',
25521                 handler: function() {
25522                     //this.value = '';
25523                     this.fireEvent("select", this, '');
25524                 },
25525                 scope: this
25526             });
25527         }
25528         
25529         
25530         if(Roo.isIE){
25531             this.el.repaint();
25532         }
25533         this.update(this.value);
25534     },
25535
25536     createMonthPicker : function(){
25537         if(!this.monthPicker.dom.firstChild){
25538             var buf = ['<table border="0" cellspacing="0">'];
25539             for(var i = 0; i < 6; i++){
25540                 buf.push(
25541                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
25542                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
25543                     i == 0 ?
25544                     '<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>' :
25545                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
25546                 );
25547             }
25548             buf.push(
25549                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
25550                     this.okText,
25551                     '</button><button type="button" class="x-date-mp-cancel">',
25552                     this.cancelText,
25553                     '</button></td></tr>',
25554                 '</table>'
25555             );
25556             this.monthPicker.update(buf.join(''));
25557             this.monthPicker.on('click', this.onMonthClick, this);
25558             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
25559
25560             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
25561             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
25562
25563             this.mpMonths.each(function(m, a, i){
25564                 i += 1;
25565                 if((i%2) == 0){
25566                     m.dom.xmonth = 5 + Math.round(i * .5);
25567                 }else{
25568                     m.dom.xmonth = Math.round((i-1) * .5);
25569                 }
25570             });
25571         }
25572     },
25573
25574     showMonthPicker : function(){
25575         this.createMonthPicker();
25576         var size = this.el.getSize();
25577         this.monthPicker.setSize(size);
25578         this.monthPicker.child('table').setSize(size);
25579
25580         this.mpSelMonth = (this.activeDate || this.value).getMonth();
25581         this.updateMPMonth(this.mpSelMonth);
25582         this.mpSelYear = (this.activeDate || this.value).getFullYear();
25583         this.updateMPYear(this.mpSelYear);
25584
25585         this.monthPicker.slideIn('t', {duration:.2});
25586     },
25587
25588     updateMPYear : function(y){
25589         this.mpyear = y;
25590         var ys = this.mpYears.elements;
25591         for(var i = 1; i <= 10; i++){
25592             var td = ys[i-1], y2;
25593             if((i%2) == 0){
25594                 y2 = y + Math.round(i * .5);
25595                 td.firstChild.innerHTML = y2;
25596                 td.xyear = y2;
25597             }else{
25598                 y2 = y - (5-Math.round(i * .5));
25599                 td.firstChild.innerHTML = y2;
25600                 td.xyear = y2;
25601             }
25602             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25603         }
25604     },
25605
25606     updateMPMonth : function(sm){
25607         this.mpMonths.each(function(m, a, i){
25608             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25609         });
25610     },
25611
25612     selectMPMonth: function(m){
25613         
25614     },
25615
25616     onMonthClick : function(e, t){
25617         e.stopEvent();
25618         var el = new Roo.Element(t), pn;
25619         if(el.is('button.x-date-mp-cancel')){
25620             this.hideMonthPicker();
25621         }
25622         else if(el.is('button.x-date-mp-ok')){
25623             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25624             this.hideMonthPicker();
25625         }
25626         else if(pn = el.up('td.x-date-mp-month', 2)){
25627             this.mpMonths.removeClass('x-date-mp-sel');
25628             pn.addClass('x-date-mp-sel');
25629             this.mpSelMonth = pn.dom.xmonth;
25630         }
25631         else if(pn = el.up('td.x-date-mp-year', 2)){
25632             this.mpYears.removeClass('x-date-mp-sel');
25633             pn.addClass('x-date-mp-sel');
25634             this.mpSelYear = pn.dom.xyear;
25635         }
25636         else if(el.is('a.x-date-mp-prev')){
25637             this.updateMPYear(this.mpyear-10);
25638         }
25639         else if(el.is('a.x-date-mp-next')){
25640             this.updateMPYear(this.mpyear+10);
25641         }
25642     },
25643
25644     onMonthDblClick : function(e, t){
25645         e.stopEvent();
25646         var el = new Roo.Element(t), pn;
25647         if(pn = el.up('td.x-date-mp-month', 2)){
25648             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25649             this.hideMonthPicker();
25650         }
25651         else if(pn = el.up('td.x-date-mp-year', 2)){
25652             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25653             this.hideMonthPicker();
25654         }
25655     },
25656
25657     hideMonthPicker : function(disableAnim){
25658         if(this.monthPicker){
25659             if(disableAnim === true){
25660                 this.monthPicker.hide();
25661             }else{
25662                 this.monthPicker.slideOut('t', {duration:.2});
25663             }
25664         }
25665     },
25666
25667     // private
25668     showPrevMonth : function(e){
25669         this.update(this.activeDate.add("mo", -1));
25670     },
25671
25672     // private
25673     showNextMonth : function(e){
25674         this.update(this.activeDate.add("mo", 1));
25675     },
25676
25677     // private
25678     showPrevYear : function(){
25679         this.update(this.activeDate.add("y", -1));
25680     },
25681
25682     // private
25683     showNextYear : function(){
25684         this.update(this.activeDate.add("y", 1));
25685     },
25686
25687     // private
25688     handleMouseWheel : function(e){
25689         var delta = e.getWheelDelta();
25690         if(delta > 0){
25691             this.showPrevMonth();
25692             e.stopEvent();
25693         } else if(delta < 0){
25694             this.showNextMonth();
25695             e.stopEvent();
25696         }
25697     },
25698
25699     // private
25700     handleDateClick : function(e, t){
25701         e.stopEvent();
25702         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25703             this.setValue(new Date(t.dateValue));
25704             this.fireEvent("select", this, this.value);
25705         }
25706     },
25707
25708     // private
25709     selectToday : function(){
25710         this.setValue(new Date().clearTime());
25711         this.fireEvent("select", this, this.value);
25712     },
25713
25714     // private
25715     update : function(date)
25716     {
25717         var vd = this.activeDate;
25718         this.activeDate = date;
25719         if(vd && this.el){
25720             var t = date.getTime();
25721             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25722                 this.cells.removeClass("x-date-selected");
25723                 this.cells.each(function(c){
25724                    if(c.dom.firstChild.dateValue == t){
25725                        c.addClass("x-date-selected");
25726                        setTimeout(function(){
25727                             try{c.dom.firstChild.focus();}catch(e){}
25728                        }, 50);
25729                        return false;
25730                    }
25731                 });
25732                 return;
25733             }
25734         }
25735         
25736         var days = date.getDaysInMonth();
25737         var firstOfMonth = date.getFirstDateOfMonth();
25738         var startingPos = firstOfMonth.getDay()-this.startDay;
25739
25740         if(startingPos <= this.startDay){
25741             startingPos += 7;
25742         }
25743
25744         var pm = date.add("mo", -1);
25745         var prevStart = pm.getDaysInMonth()-startingPos;
25746
25747         var cells = this.cells.elements;
25748         var textEls = this.textNodes;
25749         days += startingPos;
25750
25751         // convert everything to numbers so it's fast
25752         var day = 86400000;
25753         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25754         var today = new Date().clearTime().getTime();
25755         var sel = date.clearTime().getTime();
25756         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25757         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25758         var ddMatch = this.disabledDatesRE;
25759         var ddText = this.disabledDatesText;
25760         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25761         var ddaysText = this.disabledDaysText;
25762         var format = this.format;
25763
25764         var setCellClass = function(cal, cell){
25765             cell.title = "";
25766             var t = d.getTime();
25767             cell.firstChild.dateValue = t;
25768             if(t == today){
25769                 cell.className += " x-date-today";
25770                 cell.title = cal.todayText;
25771             }
25772             if(t == sel){
25773                 cell.className += " x-date-selected";
25774                 setTimeout(function(){
25775                     try{cell.firstChild.focus();}catch(e){}
25776                 }, 50);
25777             }
25778             // disabling
25779             if(t < min) {
25780                 cell.className = " x-date-disabled";
25781                 cell.title = cal.minText;
25782                 return;
25783             }
25784             if(t > max) {
25785                 cell.className = " x-date-disabled";
25786                 cell.title = cal.maxText;
25787                 return;
25788             }
25789             if(ddays){
25790                 if(ddays.indexOf(d.getDay()) != -1){
25791                     cell.title = ddaysText;
25792                     cell.className = " x-date-disabled";
25793                 }
25794             }
25795             if(ddMatch && format){
25796                 var fvalue = d.dateFormat(format);
25797                 if(ddMatch.test(fvalue)){
25798                     cell.title = ddText.replace("%0", fvalue);
25799                     cell.className = " x-date-disabled";
25800                 }
25801             }
25802         };
25803
25804         var i = 0;
25805         for(; i < startingPos; i++) {
25806             textEls[i].innerHTML = (++prevStart);
25807             d.setDate(d.getDate()+1);
25808             cells[i].className = "x-date-prevday";
25809             setCellClass(this, cells[i]);
25810         }
25811         for(; i < days; i++){
25812             intDay = i - startingPos + 1;
25813             textEls[i].innerHTML = (intDay);
25814             d.setDate(d.getDate()+1);
25815             cells[i].className = "x-date-active";
25816             setCellClass(this, cells[i]);
25817         }
25818         var extraDays = 0;
25819         for(; i < 42; i++) {
25820              textEls[i].innerHTML = (++extraDays);
25821              d.setDate(d.getDate()+1);
25822              cells[i].className = "x-date-nextday";
25823              setCellClass(this, cells[i]);
25824         }
25825
25826         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25827         this.fireEvent('monthchange', this, date);
25828         
25829         if(!this.internalRender){
25830             var main = this.el.dom.firstChild;
25831             var w = main.offsetWidth;
25832             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25833             Roo.fly(main).setWidth(w);
25834             this.internalRender = true;
25835             // opera does not respect the auto grow header center column
25836             // then, after it gets a width opera refuses to recalculate
25837             // without a second pass
25838             if(Roo.isOpera && !this.secondPass){
25839                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25840                 this.secondPass = true;
25841                 this.update.defer(10, this, [date]);
25842             }
25843         }
25844         
25845         
25846     }
25847 });        /*
25848  * Based on:
25849  * Ext JS Library 1.1.1
25850  * Copyright(c) 2006-2007, Ext JS, LLC.
25851  *
25852  * Originally Released Under LGPL - original licence link has changed is not relivant.
25853  *
25854  * Fork - LGPL
25855  * <script type="text/javascript">
25856  */
25857 /**
25858  * @class Roo.TabPanel
25859  * @extends Roo.util.Observable
25860  * A lightweight tab container.
25861  * <br><br>
25862  * Usage:
25863  * <pre><code>
25864 // basic tabs 1, built from existing content
25865 var tabs = new Roo.TabPanel("tabs1");
25866 tabs.addTab("script", "View Script");
25867 tabs.addTab("markup", "View Markup");
25868 tabs.activate("script");
25869
25870 // more advanced tabs, built from javascript
25871 var jtabs = new Roo.TabPanel("jtabs");
25872 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25873
25874 // set up the UpdateManager
25875 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25876 var updater = tab2.getUpdateManager();
25877 updater.setDefaultUrl("ajax1.htm");
25878 tab2.on('activate', updater.refresh, updater, true);
25879
25880 // Use setUrl for Ajax loading
25881 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25882 tab3.setUrl("ajax2.htm", null, true);
25883
25884 // Disabled tab
25885 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25886 tab4.disable();
25887
25888 jtabs.activate("jtabs-1");
25889  * </code></pre>
25890  * @constructor
25891  * Create a new TabPanel.
25892  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25893  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25894  */
25895 Roo.TabPanel = function(container, config){
25896     /**
25897     * The container element for this TabPanel.
25898     * @type Roo.Element
25899     */
25900     this.el = Roo.get(container, true);
25901     if(config){
25902         if(typeof config == "boolean"){
25903             this.tabPosition = config ? "bottom" : "top";
25904         }else{
25905             Roo.apply(this, config);
25906         }
25907     }
25908     if(this.tabPosition == "bottom"){
25909         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25910         this.el.addClass("x-tabs-bottom");
25911     }
25912     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25913     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25914     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25915     if(Roo.isIE){
25916         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25917     }
25918     if(this.tabPosition != "bottom"){
25919         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25920          * @type Roo.Element
25921          */
25922         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25923         this.el.addClass("x-tabs-top");
25924     }
25925     this.items = [];
25926
25927     this.bodyEl.setStyle("position", "relative");
25928
25929     this.active = null;
25930     this.activateDelegate = this.activate.createDelegate(this);
25931
25932     this.addEvents({
25933         /**
25934          * @event tabchange
25935          * Fires when the active tab changes
25936          * @param {Roo.TabPanel} this
25937          * @param {Roo.TabPanelItem} activePanel The new active tab
25938          */
25939         "tabchange": true,
25940         /**
25941          * @event beforetabchange
25942          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25943          * @param {Roo.TabPanel} this
25944          * @param {Object} e Set cancel to true on this object to cancel the tab change
25945          * @param {Roo.TabPanelItem} tab The tab being changed to
25946          */
25947         "beforetabchange" : true
25948     });
25949
25950     Roo.EventManager.onWindowResize(this.onResize, this);
25951     this.cpad = this.el.getPadding("lr");
25952     this.hiddenCount = 0;
25953
25954
25955     // toolbar on the tabbar support...
25956     if (this.toolbar) {
25957         var tcfg = this.toolbar;
25958         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25959         this.toolbar = new Roo.Toolbar(tcfg);
25960         if (Roo.isSafari) {
25961             var tbl = tcfg.container.child('table', true);
25962             tbl.setAttribute('width', '100%');
25963         }
25964         
25965     }
25966    
25967
25968
25969     Roo.TabPanel.superclass.constructor.call(this);
25970 };
25971
25972 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25973     /*
25974      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25975      */
25976     tabPosition : "top",
25977     /*
25978      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25979      */
25980     currentTabWidth : 0,
25981     /*
25982      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25983      */
25984     minTabWidth : 40,
25985     /*
25986      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25987      */
25988     maxTabWidth : 250,
25989     /*
25990      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25991      */
25992     preferredTabWidth : 175,
25993     /*
25994      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25995      */
25996     resizeTabs : false,
25997     /*
25998      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25999      */
26000     monitorResize : true,
26001     /*
26002      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26003      */
26004     toolbar : false,
26005
26006     /**
26007      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26008      * @param {String} id The id of the div to use <b>or create</b>
26009      * @param {String} text The text for the tab
26010      * @param {String} content (optional) Content to put in the TabPanelItem body
26011      * @param {Boolean} closable (optional) True to create a close icon on the tab
26012      * @return {Roo.TabPanelItem} The created TabPanelItem
26013      */
26014     addTab : function(id, text, content, closable){
26015         var item = new Roo.TabPanelItem(this, id, text, closable);
26016         this.addTabItem(item);
26017         if(content){
26018             item.setContent(content);
26019         }
26020         return item;
26021     },
26022
26023     /**
26024      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26025      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26026      * @return {Roo.TabPanelItem}
26027      */
26028     getTab : function(id){
26029         return this.items[id];
26030     },
26031
26032     /**
26033      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26034      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26035      */
26036     hideTab : function(id){
26037         var t = this.items[id];
26038         if(!t.isHidden()){
26039            t.setHidden(true);
26040            this.hiddenCount++;
26041            this.autoSizeTabs();
26042         }
26043     },
26044
26045     /**
26046      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26047      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26048      */
26049     unhideTab : function(id){
26050         var t = this.items[id];
26051         if(t.isHidden()){
26052            t.setHidden(false);
26053            this.hiddenCount--;
26054            this.autoSizeTabs();
26055         }
26056     },
26057
26058     /**
26059      * Adds an existing {@link Roo.TabPanelItem}.
26060      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26061      */
26062     addTabItem : function(item){
26063         this.items[item.id] = item;
26064         this.items.push(item);
26065         if(this.resizeTabs){
26066            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26067            this.autoSizeTabs();
26068         }else{
26069             item.autoSize();
26070         }
26071     },
26072
26073     /**
26074      * Removes a {@link Roo.TabPanelItem}.
26075      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26076      */
26077     removeTab : function(id){
26078         var items = this.items;
26079         var tab = items[id];
26080         if(!tab) { return; }
26081         var index = items.indexOf(tab);
26082         if(this.active == tab && items.length > 1){
26083             var newTab = this.getNextAvailable(index);
26084             if(newTab) {
26085                 newTab.activate();
26086             }
26087         }
26088         this.stripEl.dom.removeChild(tab.pnode.dom);
26089         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26090             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26091         }
26092         items.splice(index, 1);
26093         delete this.items[tab.id];
26094         tab.fireEvent("close", tab);
26095         tab.purgeListeners();
26096         this.autoSizeTabs();
26097     },
26098
26099     getNextAvailable : function(start){
26100         var items = this.items;
26101         var index = start;
26102         // look for a next tab that will slide over to
26103         // replace the one being removed
26104         while(index < items.length){
26105             var item = items[++index];
26106             if(item && !item.isHidden()){
26107                 return item;
26108             }
26109         }
26110         // if one isn't found select the previous tab (on the left)
26111         index = start;
26112         while(index >= 0){
26113             var item = items[--index];
26114             if(item && !item.isHidden()){
26115                 return item;
26116             }
26117         }
26118         return null;
26119     },
26120
26121     /**
26122      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26123      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26124      */
26125     disableTab : function(id){
26126         var tab = this.items[id];
26127         if(tab && this.active != tab){
26128             tab.disable();
26129         }
26130     },
26131
26132     /**
26133      * Enables a {@link Roo.TabPanelItem} that is disabled.
26134      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26135      */
26136     enableTab : function(id){
26137         var tab = this.items[id];
26138         tab.enable();
26139     },
26140
26141     /**
26142      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26143      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26144      * @return {Roo.TabPanelItem} The TabPanelItem.
26145      */
26146     activate : function(id){
26147         var tab = this.items[id];
26148         if(!tab){
26149             return null;
26150         }
26151         if(tab == this.active || tab.disabled){
26152             return tab;
26153         }
26154         var e = {};
26155         this.fireEvent("beforetabchange", this, e, tab);
26156         if(e.cancel !== true && !tab.disabled){
26157             if(this.active){
26158                 this.active.hide();
26159             }
26160             this.active = this.items[id];
26161             this.active.show();
26162             this.fireEvent("tabchange", this, this.active);
26163         }
26164         return tab;
26165     },
26166
26167     /**
26168      * Gets the active {@link Roo.TabPanelItem}.
26169      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26170      */
26171     getActiveTab : function(){
26172         return this.active;
26173     },
26174
26175     /**
26176      * Updates the tab body element to fit the height of the container element
26177      * for overflow scrolling
26178      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26179      */
26180     syncHeight : function(targetHeight){
26181         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26182         var bm = this.bodyEl.getMargins();
26183         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26184         this.bodyEl.setHeight(newHeight);
26185         return newHeight;
26186     },
26187
26188     onResize : function(){
26189         if(this.monitorResize){
26190             this.autoSizeTabs();
26191         }
26192     },
26193
26194     /**
26195      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26196      */
26197     beginUpdate : function(){
26198         this.updating = true;
26199     },
26200
26201     /**
26202      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26203      */
26204     endUpdate : function(){
26205         this.updating = false;
26206         this.autoSizeTabs();
26207     },
26208
26209     /**
26210      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26211      */
26212     autoSizeTabs : function(){
26213         var count = this.items.length;
26214         var vcount = count - this.hiddenCount;
26215         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26216         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26217         var availWidth = Math.floor(w / vcount);
26218         var b = this.stripBody;
26219         if(b.getWidth() > w){
26220             var tabs = this.items;
26221             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26222             if(availWidth < this.minTabWidth){
26223                 /*if(!this.sleft){    // incomplete scrolling code
26224                     this.createScrollButtons();
26225                 }
26226                 this.showScroll();
26227                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26228             }
26229         }else{
26230             if(this.currentTabWidth < this.preferredTabWidth){
26231                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26232             }
26233         }
26234     },
26235
26236     /**
26237      * Returns the number of tabs in this TabPanel.
26238      * @return {Number}
26239      */
26240      getCount : function(){
26241          return this.items.length;
26242      },
26243
26244     /**
26245      * Resizes all the tabs to the passed width
26246      * @param {Number} The new width
26247      */
26248     setTabWidth : function(width){
26249         this.currentTabWidth = width;
26250         for(var i = 0, len = this.items.length; i < len; i++) {
26251                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26252         }
26253     },
26254
26255     /**
26256      * Destroys this TabPanel
26257      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26258      */
26259     destroy : function(removeEl){
26260         Roo.EventManager.removeResizeListener(this.onResize, this);
26261         for(var i = 0, len = this.items.length; i < len; i++){
26262             this.items[i].purgeListeners();
26263         }
26264         if(removeEl === true){
26265             this.el.update("");
26266             this.el.remove();
26267         }
26268     }
26269 });
26270
26271 /**
26272  * @class Roo.TabPanelItem
26273  * @extends Roo.util.Observable
26274  * Represents an individual item (tab plus body) in a TabPanel.
26275  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26276  * @param {String} id The id of this TabPanelItem
26277  * @param {String} text The text for the tab of this TabPanelItem
26278  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26279  */
26280 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26281     /**
26282      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26283      * @type Roo.TabPanel
26284      */
26285     this.tabPanel = tabPanel;
26286     /**
26287      * The id for this TabPanelItem
26288      * @type String
26289      */
26290     this.id = id;
26291     /** @private */
26292     this.disabled = false;
26293     /** @private */
26294     this.text = text;
26295     /** @private */
26296     this.loaded = false;
26297     this.closable = closable;
26298
26299     /**
26300      * The body element for this TabPanelItem.
26301      * @type Roo.Element
26302      */
26303     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26304     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26305     this.bodyEl.setStyle("display", "block");
26306     this.bodyEl.setStyle("zoom", "1");
26307     this.hideAction();
26308
26309     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26310     /** @private */
26311     this.el = Roo.get(els.el, true);
26312     this.inner = Roo.get(els.inner, true);
26313     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26314     this.pnode = Roo.get(els.el.parentNode, true);
26315     this.el.on("mousedown", this.onTabMouseDown, this);
26316     this.el.on("click", this.onTabClick, this);
26317     /** @private */
26318     if(closable){
26319         var c = Roo.get(els.close, true);
26320         c.dom.title = this.closeText;
26321         c.addClassOnOver("close-over");
26322         c.on("click", this.closeClick, this);
26323      }
26324
26325     this.addEvents({
26326          /**
26327          * @event activate
26328          * Fires when this tab becomes the active tab.
26329          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26330          * @param {Roo.TabPanelItem} this
26331          */
26332         "activate": true,
26333         /**
26334          * @event beforeclose
26335          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26336          * @param {Roo.TabPanelItem} this
26337          * @param {Object} e Set cancel to true on this object to cancel the close.
26338          */
26339         "beforeclose": true,
26340         /**
26341          * @event close
26342          * Fires when this tab is closed.
26343          * @param {Roo.TabPanelItem} this
26344          */
26345          "close": true,
26346         /**
26347          * @event deactivate
26348          * Fires when this tab is no longer the active tab.
26349          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26350          * @param {Roo.TabPanelItem} this
26351          */
26352          "deactivate" : true
26353     });
26354     this.hidden = false;
26355
26356     Roo.TabPanelItem.superclass.constructor.call(this);
26357 };
26358
26359 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26360     purgeListeners : function(){
26361        Roo.util.Observable.prototype.purgeListeners.call(this);
26362        this.el.removeAllListeners();
26363     },
26364     /**
26365      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26366      */
26367     show : function(){
26368         this.pnode.addClass("on");
26369         this.showAction();
26370         if(Roo.isOpera){
26371             this.tabPanel.stripWrap.repaint();
26372         }
26373         this.fireEvent("activate", this.tabPanel, this);
26374     },
26375
26376     /**
26377      * Returns true if this tab is the active tab.
26378      * @return {Boolean}
26379      */
26380     isActive : function(){
26381         return this.tabPanel.getActiveTab() == this;
26382     },
26383
26384     /**
26385      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
26386      */
26387     hide : function(){
26388         this.pnode.removeClass("on");
26389         this.hideAction();
26390         this.fireEvent("deactivate", this.tabPanel, this);
26391     },
26392
26393     hideAction : function(){
26394         this.bodyEl.hide();
26395         this.bodyEl.setStyle("position", "absolute");
26396         this.bodyEl.setLeft("-20000px");
26397         this.bodyEl.setTop("-20000px");
26398     },
26399
26400     showAction : function(){
26401         this.bodyEl.setStyle("position", "relative");
26402         this.bodyEl.setTop("");
26403         this.bodyEl.setLeft("");
26404         this.bodyEl.show();
26405     },
26406
26407     /**
26408      * Set the tooltip for the tab.
26409      * @param {String} tooltip The tab's tooltip
26410      */
26411     setTooltip : function(text){
26412         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
26413             this.textEl.dom.qtip = text;
26414             this.textEl.dom.removeAttribute('title');
26415         }else{
26416             this.textEl.dom.title = text;
26417         }
26418     },
26419
26420     onTabClick : function(e){
26421         e.preventDefault();
26422         this.tabPanel.activate(this.id);
26423     },
26424
26425     onTabMouseDown : function(e){
26426         e.preventDefault();
26427         this.tabPanel.activate(this.id);
26428     },
26429
26430     getWidth : function(){
26431         return this.inner.getWidth();
26432     },
26433
26434     setWidth : function(width){
26435         var iwidth = width - this.pnode.getPadding("lr");
26436         this.inner.setWidth(iwidth);
26437         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
26438         this.pnode.setWidth(width);
26439     },
26440
26441     /**
26442      * Show or hide the tab
26443      * @param {Boolean} hidden True to hide or false to show.
26444      */
26445     setHidden : function(hidden){
26446         this.hidden = hidden;
26447         this.pnode.setStyle("display", hidden ? "none" : "");
26448     },
26449
26450     /**
26451      * Returns true if this tab is "hidden"
26452      * @return {Boolean}
26453      */
26454     isHidden : function(){
26455         return this.hidden;
26456     },
26457
26458     /**
26459      * Returns the text for this tab
26460      * @return {String}
26461      */
26462     getText : function(){
26463         return this.text;
26464     },
26465
26466     autoSize : function(){
26467         //this.el.beginMeasure();
26468         this.textEl.setWidth(1);
26469         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
26470         //this.el.endMeasure();
26471     },
26472
26473     /**
26474      * Sets the text for the tab (Note: this also sets the tooltip text)
26475      * @param {String} text The tab's text and tooltip
26476      */
26477     setText : function(text){
26478         this.text = text;
26479         this.textEl.update(text);
26480         this.setTooltip(text);
26481         if(!this.tabPanel.resizeTabs){
26482             this.autoSize();
26483         }
26484     },
26485     /**
26486      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
26487      */
26488     activate : function(){
26489         this.tabPanel.activate(this.id);
26490     },
26491
26492     /**
26493      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
26494      */
26495     disable : function(){
26496         if(this.tabPanel.active != this){
26497             this.disabled = true;
26498             this.pnode.addClass("disabled");
26499         }
26500     },
26501
26502     /**
26503      * Enables this TabPanelItem if it was previously disabled.
26504      */
26505     enable : function(){
26506         this.disabled = false;
26507         this.pnode.removeClass("disabled");
26508     },
26509
26510     /**
26511      * Sets the content for this TabPanelItem.
26512      * @param {String} content The content
26513      * @param {Boolean} loadScripts true to look for and load scripts
26514      */
26515     setContent : function(content, loadScripts){
26516         this.bodyEl.update(content, loadScripts);
26517     },
26518
26519     /**
26520      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
26521      * @return {Roo.UpdateManager} The UpdateManager
26522      */
26523     getUpdateManager : function(){
26524         return this.bodyEl.getUpdateManager();
26525     },
26526
26527     /**
26528      * Set a URL to be used to load the content for this TabPanelItem.
26529      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
26530      * @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)
26531      * @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)
26532      * @return {Roo.UpdateManager} The UpdateManager
26533      */
26534     setUrl : function(url, params, loadOnce){
26535         if(this.refreshDelegate){
26536             this.un('activate', this.refreshDelegate);
26537         }
26538         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
26539         this.on("activate", this.refreshDelegate);
26540         return this.bodyEl.getUpdateManager();
26541     },
26542
26543     /** @private */
26544     _handleRefresh : function(url, params, loadOnce){
26545         if(!loadOnce || !this.loaded){
26546             var updater = this.bodyEl.getUpdateManager();
26547             updater.update(url, params, this._setLoaded.createDelegate(this));
26548         }
26549     },
26550
26551     /**
26552      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
26553      *   Will fail silently if the setUrl method has not been called.
26554      *   This does not activate the panel, just updates its content.
26555      */
26556     refresh : function(){
26557         if(this.refreshDelegate){
26558            this.loaded = false;
26559            this.refreshDelegate();
26560         }
26561     },
26562
26563     /** @private */
26564     _setLoaded : function(){
26565         this.loaded = true;
26566     },
26567
26568     /** @private */
26569     closeClick : function(e){
26570         var o = {};
26571         e.stopEvent();
26572         this.fireEvent("beforeclose", this, o);
26573         if(o.cancel !== true){
26574             this.tabPanel.removeTab(this.id);
26575         }
26576     },
26577     /**
26578      * The text displayed in the tooltip for the close icon.
26579      * @type String
26580      */
26581     closeText : "Close this tab"
26582 });
26583
26584 /** @private */
26585 Roo.TabPanel.prototype.createStrip = function(container){
26586     var strip = document.createElement("div");
26587     strip.className = "x-tabs-wrap";
26588     container.appendChild(strip);
26589     return strip;
26590 };
26591 /** @private */
26592 Roo.TabPanel.prototype.createStripList = function(strip){
26593     // div wrapper for retard IE
26594     // returns the "tr" element.
26595     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26596         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26597         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26598     return strip.firstChild.firstChild.firstChild.firstChild;
26599 };
26600 /** @private */
26601 Roo.TabPanel.prototype.createBody = function(container){
26602     var body = document.createElement("div");
26603     Roo.id(body, "tab-body");
26604     Roo.fly(body).addClass("x-tabs-body");
26605     container.appendChild(body);
26606     return body;
26607 };
26608 /** @private */
26609 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26610     var body = Roo.getDom(id);
26611     if(!body){
26612         body = document.createElement("div");
26613         body.id = id;
26614     }
26615     Roo.fly(body).addClass("x-tabs-item-body");
26616     bodyEl.insertBefore(body, bodyEl.firstChild);
26617     return body;
26618 };
26619 /** @private */
26620 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26621     var td = document.createElement("td");
26622     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26623     //stripEl.appendChild(td);
26624     if(closable){
26625         td.className = "x-tabs-closable";
26626         if(!this.closeTpl){
26627             this.closeTpl = new Roo.Template(
26628                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26629                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26630                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26631             );
26632         }
26633         var el = this.closeTpl.overwrite(td, {"text": text});
26634         var close = el.getElementsByTagName("div")[0];
26635         var inner = el.getElementsByTagName("em")[0];
26636         return {"el": el, "close": close, "inner": inner};
26637     } else {
26638         if(!this.tabTpl){
26639             this.tabTpl = new Roo.Template(
26640                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26641                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26642             );
26643         }
26644         var el = this.tabTpl.overwrite(td, {"text": text});
26645         var inner = el.getElementsByTagName("em")[0];
26646         return {"el": el, "inner": inner};
26647     }
26648 };/*
26649  * Based on:
26650  * Ext JS Library 1.1.1
26651  * Copyright(c) 2006-2007, Ext JS, LLC.
26652  *
26653  * Originally Released Under LGPL - original licence link has changed is not relivant.
26654  *
26655  * Fork - LGPL
26656  * <script type="text/javascript">
26657  */
26658
26659 /**
26660  * @class Roo.Button
26661  * @extends Roo.util.Observable
26662  * Simple Button class
26663  * @cfg {String} text The button text
26664  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26665  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26666  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26667  * @cfg {Object} scope The scope of the handler
26668  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26669  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26670  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26671  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26672  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26673  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26674    applies if enableToggle = true)
26675  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26676  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26677   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26678  * @constructor
26679  * Create a new button
26680  * @param {Object} config The config object
26681  */
26682 Roo.Button = function(renderTo, config)
26683 {
26684     if (!config) {
26685         config = renderTo;
26686         renderTo = config.renderTo || false;
26687     }
26688     
26689     Roo.apply(this, config);
26690     this.addEvents({
26691         /**
26692              * @event click
26693              * Fires when this button is clicked
26694              * @param {Button} this
26695              * @param {EventObject} e The click event
26696              */
26697             "click" : true,
26698         /**
26699              * @event toggle
26700              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26701              * @param {Button} this
26702              * @param {Boolean} pressed
26703              */
26704             "toggle" : true,
26705         /**
26706              * @event mouseover
26707              * Fires when the mouse hovers over the button
26708              * @param {Button} this
26709              * @param {Event} e The event object
26710              */
26711         'mouseover' : true,
26712         /**
26713              * @event mouseout
26714              * Fires when the mouse exits the button
26715              * @param {Button} this
26716              * @param {Event} e The event object
26717              */
26718         'mouseout': true,
26719          /**
26720              * @event render
26721              * Fires when the button is rendered
26722              * @param {Button} this
26723              */
26724         'render': true
26725     });
26726     if(this.menu){
26727         this.menu = Roo.menu.MenuMgr.get(this.menu);
26728     }
26729     // register listeners first!!  - so render can be captured..
26730     Roo.util.Observable.call(this);
26731     if(renderTo){
26732         this.render(renderTo);
26733     }
26734     
26735   
26736 };
26737
26738 Roo.extend(Roo.Button, Roo.util.Observable, {
26739     /**
26740      * 
26741      */
26742     
26743     /**
26744      * Read-only. True if this button is hidden
26745      * @type Boolean
26746      */
26747     hidden : false,
26748     /**
26749      * Read-only. True if this button is disabled
26750      * @type Boolean
26751      */
26752     disabled : false,
26753     /**
26754      * Read-only. True if this button is pressed (only if enableToggle = true)
26755      * @type Boolean
26756      */
26757     pressed : false,
26758
26759     /**
26760      * @cfg {Number} tabIndex 
26761      * The DOM tabIndex for this button (defaults to undefined)
26762      */
26763     tabIndex : undefined,
26764
26765     /**
26766      * @cfg {Boolean} enableToggle
26767      * True to enable pressed/not pressed toggling (defaults to false)
26768      */
26769     enableToggle: false,
26770     /**
26771      * @cfg {Mixed} menu
26772      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26773      */
26774     menu : undefined,
26775     /**
26776      * @cfg {String} menuAlign
26777      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26778      */
26779     menuAlign : "tl-bl?",
26780
26781     /**
26782      * @cfg {String} iconCls
26783      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26784      */
26785     iconCls : undefined,
26786     /**
26787      * @cfg {String} type
26788      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26789      */
26790     type : 'button',
26791
26792     // private
26793     menuClassTarget: 'tr',
26794
26795     /**
26796      * @cfg {String} clickEvent
26797      * The type of event to map to the button's event handler (defaults to 'click')
26798      */
26799     clickEvent : 'click',
26800
26801     /**
26802      * @cfg {Boolean} handleMouseEvents
26803      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26804      */
26805     handleMouseEvents : true,
26806
26807     /**
26808      * @cfg {String} tooltipType
26809      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26810      */
26811     tooltipType : 'qtip',
26812
26813     /**
26814      * @cfg {String} cls
26815      * A CSS class to apply to the button's main element.
26816      */
26817     
26818     /**
26819      * @cfg {Roo.Template} template (Optional)
26820      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26821      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26822      * require code modifications if required elements (e.g. a button) aren't present.
26823      */
26824
26825     // private
26826     render : function(renderTo){
26827         var btn;
26828         if(this.hideParent){
26829             this.parentEl = Roo.get(renderTo);
26830         }
26831         if(!this.dhconfig){
26832             if(!this.template){
26833                 if(!Roo.Button.buttonTemplate){
26834                     // hideous table template
26835                     Roo.Button.buttonTemplate = new Roo.Template(
26836                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26837                         '<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>',
26838                         "</tr></tbody></table>");
26839                 }
26840                 this.template = Roo.Button.buttonTemplate;
26841             }
26842             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26843             var btnEl = btn.child("button:first");
26844             btnEl.on('focus', this.onFocus, this);
26845             btnEl.on('blur', this.onBlur, this);
26846             if(this.cls){
26847                 btn.addClass(this.cls);
26848             }
26849             if(this.icon){
26850                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26851             }
26852             if(this.iconCls){
26853                 btnEl.addClass(this.iconCls);
26854                 if(!this.cls){
26855                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26856                 }
26857             }
26858             if(this.tabIndex !== undefined){
26859                 btnEl.dom.tabIndex = this.tabIndex;
26860             }
26861             if(this.tooltip){
26862                 if(typeof this.tooltip == 'object'){
26863                     Roo.QuickTips.tips(Roo.apply({
26864                           target: btnEl.id
26865                     }, this.tooltip));
26866                 } else {
26867                     btnEl.dom[this.tooltipType] = this.tooltip;
26868                 }
26869             }
26870         }else{
26871             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26872         }
26873         this.el = btn;
26874         if(this.id){
26875             this.el.dom.id = this.el.id = this.id;
26876         }
26877         if(this.menu){
26878             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26879             this.menu.on("show", this.onMenuShow, this);
26880             this.menu.on("hide", this.onMenuHide, this);
26881         }
26882         btn.addClass("x-btn");
26883         if(Roo.isIE && !Roo.isIE7){
26884             this.autoWidth.defer(1, this);
26885         }else{
26886             this.autoWidth();
26887         }
26888         if(this.handleMouseEvents){
26889             btn.on("mouseover", this.onMouseOver, this);
26890             btn.on("mouseout", this.onMouseOut, this);
26891             btn.on("mousedown", this.onMouseDown, this);
26892         }
26893         btn.on(this.clickEvent, this.onClick, this);
26894         //btn.on("mouseup", this.onMouseUp, this);
26895         if(this.hidden){
26896             this.hide();
26897         }
26898         if(this.disabled){
26899             this.disable();
26900         }
26901         Roo.ButtonToggleMgr.register(this);
26902         if(this.pressed){
26903             this.el.addClass("x-btn-pressed");
26904         }
26905         if(this.repeat){
26906             var repeater = new Roo.util.ClickRepeater(btn,
26907                 typeof this.repeat == "object" ? this.repeat : {}
26908             );
26909             repeater.on("click", this.onClick,  this);
26910         }
26911         
26912         this.fireEvent('render', this);
26913         
26914     },
26915     /**
26916      * Returns the button's underlying element
26917      * @return {Roo.Element} The element
26918      */
26919     getEl : function(){
26920         return this.el;  
26921     },
26922     
26923     /**
26924      * Destroys this Button and removes any listeners.
26925      */
26926     destroy : function(){
26927         Roo.ButtonToggleMgr.unregister(this);
26928         this.el.removeAllListeners();
26929         this.purgeListeners();
26930         this.el.remove();
26931     },
26932
26933     // private
26934     autoWidth : function(){
26935         if(this.el){
26936             this.el.setWidth("auto");
26937             if(Roo.isIE7 && Roo.isStrict){
26938                 var ib = this.el.child('button');
26939                 if(ib && ib.getWidth() > 20){
26940                     ib.clip();
26941                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26942                 }
26943             }
26944             if(this.minWidth){
26945                 if(this.hidden){
26946                     this.el.beginMeasure();
26947                 }
26948                 if(this.el.getWidth() < this.minWidth){
26949                     this.el.setWidth(this.minWidth);
26950                 }
26951                 if(this.hidden){
26952                     this.el.endMeasure();
26953                 }
26954             }
26955         }
26956     },
26957
26958     /**
26959      * Assigns this button's click handler
26960      * @param {Function} handler The function to call when the button is clicked
26961      * @param {Object} scope (optional) Scope for the function passed in
26962      */
26963     setHandler : function(handler, scope){
26964         this.handler = handler;
26965         this.scope = scope;  
26966     },
26967     
26968     /**
26969      * Sets this button's text
26970      * @param {String} text The button text
26971      */
26972     setText : function(text){
26973         this.text = text;
26974         if(this.el){
26975             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26976         }
26977         this.autoWidth();
26978     },
26979     
26980     /**
26981      * Gets the text for this button
26982      * @return {String} The button text
26983      */
26984     getText : function(){
26985         return this.text;  
26986     },
26987     
26988     /**
26989      * Show this button
26990      */
26991     show: function(){
26992         this.hidden = false;
26993         if(this.el){
26994             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26995         }
26996     },
26997     
26998     /**
26999      * Hide this button
27000      */
27001     hide: function(){
27002         this.hidden = true;
27003         if(this.el){
27004             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27005         }
27006     },
27007     
27008     /**
27009      * Convenience function for boolean show/hide
27010      * @param {Boolean} visible True to show, false to hide
27011      */
27012     setVisible: function(visible){
27013         if(visible) {
27014             this.show();
27015         }else{
27016             this.hide();
27017         }
27018     },
27019     
27020     /**
27021      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27022      * @param {Boolean} state (optional) Force a particular state
27023      */
27024     toggle : function(state){
27025         state = state === undefined ? !this.pressed : state;
27026         if(state != this.pressed){
27027             if(state){
27028                 this.el.addClass("x-btn-pressed");
27029                 this.pressed = true;
27030                 this.fireEvent("toggle", this, true);
27031             }else{
27032                 this.el.removeClass("x-btn-pressed");
27033                 this.pressed = false;
27034                 this.fireEvent("toggle", this, false);
27035             }
27036             if(this.toggleHandler){
27037                 this.toggleHandler.call(this.scope || this, this, state);
27038             }
27039         }
27040     },
27041     
27042     /**
27043      * Focus the button
27044      */
27045     focus : function(){
27046         this.el.child('button:first').focus();
27047     },
27048     
27049     /**
27050      * Disable this button
27051      */
27052     disable : function(){
27053         if(this.el){
27054             this.el.addClass("x-btn-disabled");
27055         }
27056         this.disabled = true;
27057     },
27058     
27059     /**
27060      * Enable this button
27061      */
27062     enable : function(){
27063         if(this.el){
27064             this.el.removeClass("x-btn-disabled");
27065         }
27066         this.disabled = false;
27067     },
27068
27069     /**
27070      * Convenience function for boolean enable/disable
27071      * @param {Boolean} enabled True to enable, false to disable
27072      */
27073     setDisabled : function(v){
27074         this[v !== true ? "enable" : "disable"]();
27075     },
27076
27077     // private
27078     onClick : function(e){
27079         if(e){
27080             e.preventDefault();
27081         }
27082         if(e.button != 0){
27083             return;
27084         }
27085         if(!this.disabled){
27086             if(this.enableToggle){
27087                 this.toggle();
27088             }
27089             if(this.menu && !this.menu.isVisible()){
27090                 this.menu.show(this.el, this.menuAlign);
27091             }
27092             this.fireEvent("click", this, e);
27093             if(this.handler){
27094                 this.el.removeClass("x-btn-over");
27095                 this.handler.call(this.scope || this, this, e);
27096             }
27097         }
27098     },
27099     // private
27100     onMouseOver : function(e){
27101         if(!this.disabled){
27102             this.el.addClass("x-btn-over");
27103             this.fireEvent('mouseover', this, e);
27104         }
27105     },
27106     // private
27107     onMouseOut : function(e){
27108         if(!e.within(this.el,  true)){
27109             this.el.removeClass("x-btn-over");
27110             this.fireEvent('mouseout', this, e);
27111         }
27112     },
27113     // private
27114     onFocus : function(e){
27115         if(!this.disabled){
27116             this.el.addClass("x-btn-focus");
27117         }
27118     },
27119     // private
27120     onBlur : function(e){
27121         this.el.removeClass("x-btn-focus");
27122     },
27123     // private
27124     onMouseDown : function(e){
27125         if(!this.disabled && e.button == 0){
27126             this.el.addClass("x-btn-click");
27127             Roo.get(document).on('mouseup', this.onMouseUp, this);
27128         }
27129     },
27130     // private
27131     onMouseUp : function(e){
27132         if(e.button == 0){
27133             this.el.removeClass("x-btn-click");
27134             Roo.get(document).un('mouseup', this.onMouseUp, this);
27135         }
27136     },
27137     // private
27138     onMenuShow : function(e){
27139         this.el.addClass("x-btn-menu-active");
27140     },
27141     // private
27142     onMenuHide : function(e){
27143         this.el.removeClass("x-btn-menu-active");
27144     }   
27145 });
27146
27147 // Private utility class used by Button
27148 Roo.ButtonToggleMgr = function(){
27149    var groups = {};
27150    
27151    function toggleGroup(btn, state){
27152        if(state){
27153            var g = groups[btn.toggleGroup];
27154            for(var i = 0, l = g.length; i < l; i++){
27155                if(g[i] != btn){
27156                    g[i].toggle(false);
27157                }
27158            }
27159        }
27160    }
27161    
27162    return {
27163        register : function(btn){
27164            if(!btn.toggleGroup){
27165                return;
27166            }
27167            var g = groups[btn.toggleGroup];
27168            if(!g){
27169                g = groups[btn.toggleGroup] = [];
27170            }
27171            g.push(btn);
27172            btn.on("toggle", toggleGroup);
27173        },
27174        
27175        unregister : function(btn){
27176            if(!btn.toggleGroup){
27177                return;
27178            }
27179            var g = groups[btn.toggleGroup];
27180            if(g){
27181                g.remove(btn);
27182                btn.un("toggle", toggleGroup);
27183            }
27184        }
27185    };
27186 }();/*
27187  * Based on:
27188  * Ext JS Library 1.1.1
27189  * Copyright(c) 2006-2007, Ext JS, LLC.
27190  *
27191  * Originally Released Under LGPL - original licence link has changed is not relivant.
27192  *
27193  * Fork - LGPL
27194  * <script type="text/javascript">
27195  */
27196  
27197 /**
27198  * @class Roo.SplitButton
27199  * @extends Roo.Button
27200  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27201  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27202  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27203  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27204  * @cfg {String} arrowTooltip The title attribute of the arrow
27205  * @constructor
27206  * Create a new menu button
27207  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27208  * @param {Object} config The config object
27209  */
27210 Roo.SplitButton = function(renderTo, config){
27211     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27212     /**
27213      * @event arrowclick
27214      * Fires when this button's arrow is clicked
27215      * @param {SplitButton} this
27216      * @param {EventObject} e The click event
27217      */
27218     this.addEvents({"arrowclick":true});
27219 };
27220
27221 Roo.extend(Roo.SplitButton, Roo.Button, {
27222     render : function(renderTo){
27223         // this is one sweet looking template!
27224         var tpl = new Roo.Template(
27225             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27226             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27227             '<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>',
27228             "</tbody></table></td><td>",
27229             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27230             '<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>',
27231             "</tbody></table></td></tr></table>"
27232         );
27233         var btn = tpl.append(renderTo, [this.text, this.type], true);
27234         var btnEl = btn.child("button");
27235         if(this.cls){
27236             btn.addClass(this.cls);
27237         }
27238         if(this.icon){
27239             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27240         }
27241         if(this.iconCls){
27242             btnEl.addClass(this.iconCls);
27243             if(!this.cls){
27244                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27245             }
27246         }
27247         this.el = btn;
27248         if(this.handleMouseEvents){
27249             btn.on("mouseover", this.onMouseOver, this);
27250             btn.on("mouseout", this.onMouseOut, this);
27251             btn.on("mousedown", this.onMouseDown, this);
27252             btn.on("mouseup", this.onMouseUp, this);
27253         }
27254         btn.on(this.clickEvent, this.onClick, this);
27255         if(this.tooltip){
27256             if(typeof this.tooltip == 'object'){
27257                 Roo.QuickTips.tips(Roo.apply({
27258                       target: btnEl.id
27259                 }, this.tooltip));
27260             } else {
27261                 btnEl.dom[this.tooltipType] = this.tooltip;
27262             }
27263         }
27264         if(this.arrowTooltip){
27265             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27266         }
27267         if(this.hidden){
27268             this.hide();
27269         }
27270         if(this.disabled){
27271             this.disable();
27272         }
27273         if(this.pressed){
27274             this.el.addClass("x-btn-pressed");
27275         }
27276         if(Roo.isIE && !Roo.isIE7){
27277             this.autoWidth.defer(1, this);
27278         }else{
27279             this.autoWidth();
27280         }
27281         if(this.menu){
27282             this.menu.on("show", this.onMenuShow, this);
27283             this.menu.on("hide", this.onMenuHide, this);
27284         }
27285         this.fireEvent('render', this);
27286     },
27287
27288     // private
27289     autoWidth : function(){
27290         if(this.el){
27291             var tbl = this.el.child("table:first");
27292             var tbl2 = this.el.child("table:last");
27293             this.el.setWidth("auto");
27294             tbl.setWidth("auto");
27295             if(Roo.isIE7 && Roo.isStrict){
27296                 var ib = this.el.child('button:first');
27297                 if(ib && ib.getWidth() > 20){
27298                     ib.clip();
27299                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27300                 }
27301             }
27302             if(this.minWidth){
27303                 if(this.hidden){
27304                     this.el.beginMeasure();
27305                 }
27306                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27307                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27308                 }
27309                 if(this.hidden){
27310                     this.el.endMeasure();
27311                 }
27312             }
27313             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27314         } 
27315     },
27316     /**
27317      * Sets this button's click handler
27318      * @param {Function} handler The function to call when the button is clicked
27319      * @param {Object} scope (optional) Scope for the function passed above
27320      */
27321     setHandler : function(handler, scope){
27322         this.handler = handler;
27323         this.scope = scope;  
27324     },
27325     
27326     /**
27327      * Sets this button's arrow click handler
27328      * @param {Function} handler The function to call when the arrow is clicked
27329      * @param {Object} scope (optional) Scope for the function passed above
27330      */
27331     setArrowHandler : function(handler, scope){
27332         this.arrowHandler = handler;
27333         this.scope = scope;  
27334     },
27335     
27336     /**
27337      * Focus the button
27338      */
27339     focus : function(){
27340         if(this.el){
27341             this.el.child("button:first").focus();
27342         }
27343     },
27344
27345     // private
27346     onClick : function(e){
27347         e.preventDefault();
27348         if(!this.disabled){
27349             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27350                 if(this.menu && !this.menu.isVisible()){
27351                     this.menu.show(this.el, this.menuAlign);
27352                 }
27353                 this.fireEvent("arrowclick", this, e);
27354                 if(this.arrowHandler){
27355                     this.arrowHandler.call(this.scope || this, this, e);
27356                 }
27357             }else{
27358                 this.fireEvent("click", this, e);
27359                 if(this.handler){
27360                     this.handler.call(this.scope || this, this, e);
27361                 }
27362             }
27363         }
27364     },
27365     // private
27366     onMouseDown : function(e){
27367         if(!this.disabled){
27368             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27369         }
27370     },
27371     // private
27372     onMouseUp : function(e){
27373         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27374     }   
27375 });
27376
27377
27378 // backwards compat
27379 Roo.MenuButton = Roo.SplitButton;/*
27380  * Based on:
27381  * Ext JS Library 1.1.1
27382  * Copyright(c) 2006-2007, Ext JS, LLC.
27383  *
27384  * Originally Released Under LGPL - original licence link has changed is not relivant.
27385  *
27386  * Fork - LGPL
27387  * <script type="text/javascript">
27388  */
27389
27390 /**
27391  * @class Roo.Toolbar
27392  * Basic Toolbar class.
27393  * @constructor
27394  * Creates a new Toolbar
27395  * @param {Object} container The config object
27396  */ 
27397 Roo.Toolbar = function(container, buttons, config)
27398 {
27399     /// old consturctor format still supported..
27400     if(container instanceof Array){ // omit the container for later rendering
27401         buttons = container;
27402         config = buttons;
27403         container = null;
27404     }
27405     if (typeof(container) == 'object' && container.xtype) {
27406         config = container;
27407         container = config.container;
27408         buttons = config.buttons || []; // not really - use items!!
27409     }
27410     var xitems = [];
27411     if (config && config.items) {
27412         xitems = config.items;
27413         delete config.items;
27414     }
27415     Roo.apply(this, config);
27416     this.buttons = buttons;
27417     
27418     if(container){
27419         this.render(container);
27420     }
27421     this.xitems = xitems;
27422     Roo.each(xitems, function(b) {
27423         this.add(b);
27424     }, this);
27425     
27426 };
27427
27428 Roo.Toolbar.prototype = {
27429     /**
27430      * @cfg {Array} items
27431      * array of button configs or elements to add (will be converted to a MixedCollection)
27432      */
27433     
27434     /**
27435      * @cfg {String/HTMLElement/Element} container
27436      * The id or element that will contain the toolbar
27437      */
27438     // private
27439     render : function(ct){
27440         this.el = Roo.get(ct);
27441         if(this.cls){
27442             this.el.addClass(this.cls);
27443         }
27444         // using a table allows for vertical alignment
27445         // 100% width is needed by Safari...
27446         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
27447         this.tr = this.el.child("tr", true);
27448         var autoId = 0;
27449         this.items = new Roo.util.MixedCollection(false, function(o){
27450             return o.id || ("item" + (++autoId));
27451         });
27452         if(this.buttons){
27453             this.add.apply(this, this.buttons);
27454             delete this.buttons;
27455         }
27456     },
27457
27458     /**
27459      * Adds element(s) to the toolbar -- this function takes a variable number of 
27460      * arguments of mixed type and adds them to the toolbar.
27461      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
27462      * <ul>
27463      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
27464      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
27465      * <li>Field: Any form field (equivalent to {@link #addField})</li>
27466      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
27467      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
27468      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
27469      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
27470      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
27471      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
27472      * </ul>
27473      * @param {Mixed} arg2
27474      * @param {Mixed} etc.
27475      */
27476     add : function(){
27477         var a = arguments, l = a.length;
27478         for(var i = 0; i < l; i++){
27479             this._add(a[i]);
27480         }
27481     },
27482     // private..
27483     _add : function(el) {
27484         
27485         if (el.xtype) {
27486             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
27487         }
27488         
27489         if (el.applyTo){ // some kind of form field
27490             return this.addField(el);
27491         } 
27492         if (el.render){ // some kind of Toolbar.Item
27493             return this.addItem(el);
27494         }
27495         if (typeof el == "string"){ // string
27496             if(el == "separator" || el == "-"){
27497                 return this.addSeparator();
27498             }
27499             if (el == " "){
27500                 return this.addSpacer();
27501             }
27502             if(el == "->"){
27503                 return this.addFill();
27504             }
27505             return this.addText(el);
27506             
27507         }
27508         if(el.tagName){ // element
27509             return this.addElement(el);
27510         }
27511         if(typeof el == "object"){ // must be button config?
27512             return this.addButton(el);
27513         }
27514         // and now what?!?!
27515         return false;
27516         
27517     },
27518     
27519     /**
27520      * Add an Xtype element
27521      * @param {Object} xtype Xtype Object
27522      * @return {Object} created Object
27523      */
27524     addxtype : function(e){
27525         return this.add(e);  
27526     },
27527     
27528     /**
27529      * Returns the Element for this toolbar.
27530      * @return {Roo.Element}
27531      */
27532     getEl : function(){
27533         return this.el;  
27534     },
27535     
27536     /**
27537      * Adds a separator
27538      * @return {Roo.Toolbar.Item} The separator item
27539      */
27540     addSeparator : function(){
27541         return this.addItem(new Roo.Toolbar.Separator());
27542     },
27543
27544     /**
27545      * Adds a spacer element
27546      * @return {Roo.Toolbar.Spacer} The spacer item
27547      */
27548     addSpacer : function(){
27549         return this.addItem(new Roo.Toolbar.Spacer());
27550     },
27551
27552     /**
27553      * Adds a fill element that forces subsequent additions to the right side of the toolbar
27554      * @return {Roo.Toolbar.Fill} The fill item
27555      */
27556     addFill : function(){
27557         return this.addItem(new Roo.Toolbar.Fill());
27558     },
27559
27560     /**
27561      * Adds any standard HTML element to the toolbar
27562      * @param {String/HTMLElement/Element} el The element or id of the element to add
27563      * @return {Roo.Toolbar.Item} The element's item
27564      */
27565     addElement : function(el){
27566         return this.addItem(new Roo.Toolbar.Item(el));
27567     },
27568     /**
27569      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
27570      * @type Roo.util.MixedCollection  
27571      */
27572     items : false,
27573      
27574     /**
27575      * Adds any Toolbar.Item or subclass
27576      * @param {Roo.Toolbar.Item} item
27577      * @return {Roo.Toolbar.Item} The item
27578      */
27579     addItem : function(item){
27580         var td = this.nextBlock();
27581         item.render(td);
27582         this.items.add(item);
27583         return item;
27584     },
27585     
27586     /**
27587      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
27588      * @param {Object/Array} config A button config or array of configs
27589      * @return {Roo.Toolbar.Button/Array}
27590      */
27591     addButton : function(config){
27592         if(config instanceof Array){
27593             var buttons = [];
27594             for(var i = 0, len = config.length; i < len; i++) {
27595                 buttons.push(this.addButton(config[i]));
27596             }
27597             return buttons;
27598         }
27599         var b = config;
27600         if(!(config instanceof Roo.Toolbar.Button)){
27601             b = config.split ?
27602                 new Roo.Toolbar.SplitButton(config) :
27603                 new Roo.Toolbar.Button(config);
27604         }
27605         var td = this.nextBlock();
27606         b.render(td);
27607         this.items.add(b);
27608         return b;
27609     },
27610     
27611     /**
27612      * Adds text to the toolbar
27613      * @param {String} text The text to add
27614      * @return {Roo.Toolbar.Item} The element's item
27615      */
27616     addText : function(text){
27617         return this.addItem(new Roo.Toolbar.TextItem(text));
27618     },
27619     
27620     /**
27621      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27622      * @param {Number} index The index where the item is to be inserted
27623      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27624      * @return {Roo.Toolbar.Button/Item}
27625      */
27626     insertButton : function(index, item){
27627         if(item instanceof Array){
27628             var buttons = [];
27629             for(var i = 0, len = item.length; i < len; i++) {
27630                buttons.push(this.insertButton(index + i, item[i]));
27631             }
27632             return buttons;
27633         }
27634         if (!(item instanceof Roo.Toolbar.Button)){
27635            item = new Roo.Toolbar.Button(item);
27636         }
27637         var td = document.createElement("td");
27638         this.tr.insertBefore(td, this.tr.childNodes[index]);
27639         item.render(td);
27640         this.items.insert(index, item);
27641         return item;
27642     },
27643     
27644     /**
27645      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27646      * @param {Object} config
27647      * @return {Roo.Toolbar.Item} The element's item
27648      */
27649     addDom : function(config, returnEl){
27650         var td = this.nextBlock();
27651         Roo.DomHelper.overwrite(td, config);
27652         var ti = new Roo.Toolbar.Item(td.firstChild);
27653         ti.render(td);
27654         this.items.add(ti);
27655         return ti;
27656     },
27657
27658     /**
27659      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27660      * @type Roo.util.MixedCollection  
27661      */
27662     fields : false,
27663     
27664     /**
27665      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27666      * Note: the field should not have been rendered yet. For a field that has already been
27667      * rendered, use {@link #addElement}.
27668      * @param {Roo.form.Field} field
27669      * @return {Roo.ToolbarItem}
27670      */
27671      
27672       
27673     addField : function(field) {
27674         if (!this.fields) {
27675             var autoId = 0;
27676             this.fields = new Roo.util.MixedCollection(false, function(o){
27677                 return o.id || ("item" + (++autoId));
27678             });
27679
27680         }
27681         
27682         var td = this.nextBlock();
27683         field.render(td);
27684         var ti = new Roo.Toolbar.Item(td.firstChild);
27685         ti.render(td);
27686         this.items.add(ti);
27687         this.fields.add(field);
27688         return ti;
27689     },
27690     /**
27691      * Hide the toolbar
27692      * @method hide
27693      */
27694      
27695       
27696     hide : function()
27697     {
27698         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27699         this.el.child('div').hide();
27700     },
27701     /**
27702      * Show the toolbar
27703      * @method show
27704      */
27705     show : function()
27706     {
27707         this.el.child('div').show();
27708     },
27709       
27710     // private
27711     nextBlock : function(){
27712         var td = document.createElement("td");
27713         this.tr.appendChild(td);
27714         return td;
27715     },
27716
27717     // private
27718     destroy : function(){
27719         if(this.items){ // rendered?
27720             Roo.destroy.apply(Roo, this.items.items);
27721         }
27722         if(this.fields){ // rendered?
27723             Roo.destroy.apply(Roo, this.fields.items);
27724         }
27725         Roo.Element.uncache(this.el, this.tr);
27726     }
27727 };
27728
27729 /**
27730  * @class Roo.Toolbar.Item
27731  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27732  * @constructor
27733  * Creates a new Item
27734  * @param {HTMLElement} el 
27735  */
27736 Roo.Toolbar.Item = function(el){
27737     this.el = Roo.getDom(el);
27738     this.id = Roo.id(this.el);
27739     this.hidden = false;
27740 };
27741
27742 Roo.Toolbar.Item.prototype = {
27743     
27744     /**
27745      * Get this item's HTML Element
27746      * @return {HTMLElement}
27747      */
27748     getEl : function(){
27749        return this.el;  
27750     },
27751
27752     // private
27753     render : function(td){
27754         this.td = td;
27755         td.appendChild(this.el);
27756     },
27757     
27758     /**
27759      * Removes and destroys this item.
27760      */
27761     destroy : function(){
27762         this.td.parentNode.removeChild(this.td);
27763     },
27764     
27765     /**
27766      * Shows this item.
27767      */
27768     show: function(){
27769         this.hidden = false;
27770         this.td.style.display = "";
27771     },
27772     
27773     /**
27774      * Hides this item.
27775      */
27776     hide: function(){
27777         this.hidden = true;
27778         this.td.style.display = "none";
27779     },
27780     
27781     /**
27782      * Convenience function for boolean show/hide.
27783      * @param {Boolean} visible true to show/false to hide
27784      */
27785     setVisible: function(visible){
27786         if(visible) {
27787             this.show();
27788         }else{
27789             this.hide();
27790         }
27791     },
27792     
27793     /**
27794      * Try to focus this item.
27795      */
27796     focus : function(){
27797         Roo.fly(this.el).focus();
27798     },
27799     
27800     /**
27801      * Disables this item.
27802      */
27803     disable : function(){
27804         Roo.fly(this.td).addClass("x-item-disabled");
27805         this.disabled = true;
27806         this.el.disabled = true;
27807     },
27808     
27809     /**
27810      * Enables this item.
27811      */
27812     enable : function(){
27813         Roo.fly(this.td).removeClass("x-item-disabled");
27814         this.disabled = false;
27815         this.el.disabled = false;
27816     }
27817 };
27818
27819
27820 /**
27821  * @class Roo.Toolbar.Separator
27822  * @extends Roo.Toolbar.Item
27823  * A simple toolbar separator class
27824  * @constructor
27825  * Creates a new Separator
27826  */
27827 Roo.Toolbar.Separator = function(){
27828     var s = document.createElement("span");
27829     s.className = "ytb-sep";
27830     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27831 };
27832 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27833     enable:Roo.emptyFn,
27834     disable:Roo.emptyFn,
27835     focus:Roo.emptyFn
27836 });
27837
27838 /**
27839  * @class Roo.Toolbar.Spacer
27840  * @extends Roo.Toolbar.Item
27841  * A simple element that adds extra horizontal space to a toolbar.
27842  * @constructor
27843  * Creates a new Spacer
27844  */
27845 Roo.Toolbar.Spacer = function(){
27846     var s = document.createElement("div");
27847     s.className = "ytb-spacer";
27848     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27849 };
27850 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27851     enable:Roo.emptyFn,
27852     disable:Roo.emptyFn,
27853     focus:Roo.emptyFn
27854 });
27855
27856 /**
27857  * @class Roo.Toolbar.Fill
27858  * @extends Roo.Toolbar.Spacer
27859  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27860  * @constructor
27861  * Creates a new Spacer
27862  */
27863 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27864     // private
27865     render : function(td){
27866         td.style.width = '100%';
27867         Roo.Toolbar.Fill.superclass.render.call(this, td);
27868     }
27869 });
27870
27871 /**
27872  * @class Roo.Toolbar.TextItem
27873  * @extends Roo.Toolbar.Item
27874  * A simple class that renders text directly into a toolbar.
27875  * @constructor
27876  * Creates a new TextItem
27877  * @param {String} text
27878  */
27879 Roo.Toolbar.TextItem = function(text){
27880     if (typeof(text) == 'object') {
27881         text = text.text;
27882     }
27883     var s = document.createElement("span");
27884     s.className = "ytb-text";
27885     s.innerHTML = text;
27886     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27887 };
27888 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27889     enable:Roo.emptyFn,
27890     disable:Roo.emptyFn,
27891     focus:Roo.emptyFn
27892 });
27893
27894 /**
27895  * @class Roo.Toolbar.Button
27896  * @extends Roo.Button
27897  * A button that renders into a toolbar.
27898  * @constructor
27899  * Creates a new Button
27900  * @param {Object} config A standard {@link Roo.Button} config object
27901  */
27902 Roo.Toolbar.Button = function(config){
27903     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27904 };
27905 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27906     render : function(td){
27907         this.td = td;
27908         Roo.Toolbar.Button.superclass.render.call(this, td);
27909     },
27910     
27911     /**
27912      * Removes and destroys this button
27913      */
27914     destroy : function(){
27915         Roo.Toolbar.Button.superclass.destroy.call(this);
27916         this.td.parentNode.removeChild(this.td);
27917     },
27918     
27919     /**
27920      * Shows this button
27921      */
27922     show: function(){
27923         this.hidden = false;
27924         this.td.style.display = "";
27925     },
27926     
27927     /**
27928      * Hides this button
27929      */
27930     hide: function(){
27931         this.hidden = true;
27932         this.td.style.display = "none";
27933     },
27934
27935     /**
27936      * Disables this item
27937      */
27938     disable : function(){
27939         Roo.fly(this.td).addClass("x-item-disabled");
27940         this.disabled = true;
27941     },
27942
27943     /**
27944      * Enables this item
27945      */
27946     enable : function(){
27947         Roo.fly(this.td).removeClass("x-item-disabled");
27948         this.disabled = false;
27949     }
27950 });
27951 // backwards compat
27952 Roo.ToolbarButton = Roo.Toolbar.Button;
27953
27954 /**
27955  * @class Roo.Toolbar.SplitButton
27956  * @extends Roo.SplitButton
27957  * A menu button that renders into a toolbar.
27958  * @constructor
27959  * Creates a new SplitButton
27960  * @param {Object} config A standard {@link Roo.SplitButton} config object
27961  */
27962 Roo.Toolbar.SplitButton = function(config){
27963     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27964 };
27965 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27966     render : function(td){
27967         this.td = td;
27968         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27969     },
27970     
27971     /**
27972      * Removes and destroys this button
27973      */
27974     destroy : function(){
27975         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27976         this.td.parentNode.removeChild(this.td);
27977     },
27978     
27979     /**
27980      * Shows this button
27981      */
27982     show: function(){
27983         this.hidden = false;
27984         this.td.style.display = "";
27985     },
27986     
27987     /**
27988      * Hides this button
27989      */
27990     hide: function(){
27991         this.hidden = true;
27992         this.td.style.display = "none";
27993     }
27994 });
27995
27996 // backwards compat
27997 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27998  * Based on:
27999  * Ext JS Library 1.1.1
28000  * Copyright(c) 2006-2007, Ext JS, LLC.
28001  *
28002  * Originally Released Under LGPL - original licence link has changed is not relivant.
28003  *
28004  * Fork - LGPL
28005  * <script type="text/javascript">
28006  */
28007  
28008 /**
28009  * @class Roo.PagingToolbar
28010  * @extends Roo.Toolbar
28011  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28012  * @constructor
28013  * Create a new PagingToolbar
28014  * @param {Object} config The config object
28015  */
28016 Roo.PagingToolbar = function(el, ds, config)
28017 {
28018     // old args format still supported... - xtype is prefered..
28019     if (typeof(el) == 'object' && el.xtype) {
28020         // created from xtype...
28021         config = el;
28022         ds = el.dataSource;
28023         el = config.container;
28024     }
28025     var items = [];
28026     if (config.items) {
28027         items = config.items;
28028         config.items = [];
28029     }
28030     
28031     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28032     this.ds = ds;
28033     this.cursor = 0;
28034     this.renderButtons(this.el);
28035     this.bind(ds);
28036     
28037     // supprot items array.
28038    
28039     Roo.each(items, function(e) {
28040         this.add(Roo.factory(e));
28041     },this);
28042     
28043 };
28044
28045 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28046     /**
28047      * @cfg {Roo.data.Store} dataSource
28048      * The underlying data store providing the paged data
28049      */
28050     /**
28051      * @cfg {String/HTMLElement/Element} container
28052      * container The id or element that will contain the toolbar
28053      */
28054     /**
28055      * @cfg {Boolean} displayInfo
28056      * True to display the displayMsg (defaults to false)
28057      */
28058     /**
28059      * @cfg {Number} pageSize
28060      * The number of records to display per page (defaults to 20)
28061      */
28062     pageSize: 20,
28063     /**
28064      * @cfg {String} displayMsg
28065      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28066      */
28067     displayMsg : 'Displaying {0} - {1} of {2}',
28068     /**
28069      * @cfg {String} emptyMsg
28070      * The message to display when no records are found (defaults to "No data to display")
28071      */
28072     emptyMsg : 'No data to display',
28073     /**
28074      * Customizable piece of the default paging text (defaults to "Page")
28075      * @type String
28076      */
28077     beforePageText : "Page",
28078     /**
28079      * Customizable piece of the default paging text (defaults to "of %0")
28080      * @type String
28081      */
28082     afterPageText : "of {0}",
28083     /**
28084      * Customizable piece of the default paging text (defaults to "First Page")
28085      * @type String
28086      */
28087     firstText : "First Page",
28088     /**
28089      * Customizable piece of the default paging text (defaults to "Previous Page")
28090      * @type String
28091      */
28092     prevText : "Previous Page",
28093     /**
28094      * Customizable piece of the default paging text (defaults to "Next Page")
28095      * @type String
28096      */
28097     nextText : "Next Page",
28098     /**
28099      * Customizable piece of the default paging text (defaults to "Last Page")
28100      * @type String
28101      */
28102     lastText : "Last Page",
28103     /**
28104      * Customizable piece of the default paging text (defaults to "Refresh")
28105      * @type String
28106      */
28107     refreshText : "Refresh",
28108
28109     // private
28110     renderButtons : function(el){
28111         Roo.PagingToolbar.superclass.render.call(this, el);
28112         this.first = this.addButton({
28113             tooltip: this.firstText,
28114             cls: "x-btn-icon x-grid-page-first",
28115             disabled: true,
28116             handler: this.onClick.createDelegate(this, ["first"])
28117         });
28118         this.prev = this.addButton({
28119             tooltip: this.prevText,
28120             cls: "x-btn-icon x-grid-page-prev",
28121             disabled: true,
28122             handler: this.onClick.createDelegate(this, ["prev"])
28123         });
28124         //this.addSeparator();
28125         this.add(this.beforePageText);
28126         this.field = Roo.get(this.addDom({
28127            tag: "input",
28128            type: "text",
28129            size: "3",
28130            value: "1",
28131            cls: "x-grid-page-number"
28132         }).el);
28133         this.field.on("keydown", this.onPagingKeydown, this);
28134         this.field.on("focus", function(){this.dom.select();});
28135         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28136         this.field.setHeight(18);
28137         //this.addSeparator();
28138         this.next = this.addButton({
28139             tooltip: this.nextText,
28140             cls: "x-btn-icon x-grid-page-next",
28141             disabled: true,
28142             handler: this.onClick.createDelegate(this, ["next"])
28143         });
28144         this.last = this.addButton({
28145             tooltip: this.lastText,
28146             cls: "x-btn-icon x-grid-page-last",
28147             disabled: true,
28148             handler: this.onClick.createDelegate(this, ["last"])
28149         });
28150         //this.addSeparator();
28151         this.loading = this.addButton({
28152             tooltip: this.refreshText,
28153             cls: "x-btn-icon x-grid-loading",
28154             handler: this.onClick.createDelegate(this, ["refresh"])
28155         });
28156
28157         if(this.displayInfo){
28158             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28159         }
28160     },
28161
28162     // private
28163     updateInfo : function(){
28164         if(this.displayEl){
28165             var count = this.ds.getCount();
28166             var msg = count == 0 ?
28167                 this.emptyMsg :
28168                 String.format(
28169                     this.displayMsg,
28170                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28171                 );
28172             this.displayEl.update(msg);
28173         }
28174     },
28175
28176     // private
28177     onLoad : function(ds, r, o){
28178        this.cursor = o.params ? o.params.start : 0;
28179        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28180
28181        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28182        this.field.dom.value = ap;
28183        this.first.setDisabled(ap == 1);
28184        this.prev.setDisabled(ap == 1);
28185        this.next.setDisabled(ap == ps);
28186        this.last.setDisabled(ap == ps);
28187        this.loading.enable();
28188        this.updateInfo();
28189     },
28190
28191     // private
28192     getPageData : function(){
28193         var total = this.ds.getTotalCount();
28194         return {
28195             total : total,
28196             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28197             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28198         };
28199     },
28200
28201     // private
28202     onLoadError : function(){
28203         this.loading.enable();
28204     },
28205
28206     // private
28207     onPagingKeydown : function(e){
28208         var k = e.getKey();
28209         var d = this.getPageData();
28210         if(k == e.RETURN){
28211             var v = this.field.dom.value, pageNum;
28212             if(!v || isNaN(pageNum = parseInt(v, 10))){
28213                 this.field.dom.value = d.activePage;
28214                 return;
28215             }
28216             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28217             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28218             e.stopEvent();
28219         }
28220         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))
28221         {
28222           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28223           this.field.dom.value = pageNum;
28224           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28225           e.stopEvent();
28226         }
28227         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28228         {
28229           var v = this.field.dom.value, pageNum; 
28230           var increment = (e.shiftKey) ? 10 : 1;
28231           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28232             increment *= -1;
28233           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28234             this.field.dom.value = d.activePage;
28235             return;
28236           }
28237           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28238           {
28239             this.field.dom.value = parseInt(v, 10) + increment;
28240             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28241             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28242           }
28243           e.stopEvent();
28244         }
28245     },
28246
28247     // private
28248     beforeLoad : function(){
28249         if(this.loading){
28250             this.loading.disable();
28251         }
28252     },
28253
28254     // private
28255     onClick : function(which){
28256         var ds = this.ds;
28257         switch(which){
28258             case "first":
28259                 ds.load({params:{start: 0, limit: this.pageSize}});
28260             break;
28261             case "prev":
28262                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28263             break;
28264             case "next":
28265                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28266             break;
28267             case "last":
28268                 var total = ds.getTotalCount();
28269                 var extra = total % this.pageSize;
28270                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28271                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28272             break;
28273             case "refresh":
28274                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28275             break;
28276         }
28277     },
28278
28279     /**
28280      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28281      * @param {Roo.data.Store} store The data store to unbind
28282      */
28283     unbind : function(ds){
28284         ds.un("beforeload", this.beforeLoad, this);
28285         ds.un("load", this.onLoad, this);
28286         ds.un("loadexception", this.onLoadError, this);
28287         ds.un("remove", this.updateInfo, this);
28288         ds.un("add", this.updateInfo, this);
28289         this.ds = undefined;
28290     },
28291
28292     /**
28293      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28294      * @param {Roo.data.Store} store The data store to bind
28295      */
28296     bind : function(ds){
28297         ds.on("beforeload", this.beforeLoad, this);
28298         ds.on("load", this.onLoad, this);
28299         ds.on("loadexception", this.onLoadError, this);
28300         ds.on("remove", this.updateInfo, this);
28301         ds.on("add", this.updateInfo, this);
28302         this.ds = ds;
28303     }
28304 });/*
28305  * Based on:
28306  * Ext JS Library 1.1.1
28307  * Copyright(c) 2006-2007, Ext JS, LLC.
28308  *
28309  * Originally Released Under LGPL - original licence link has changed is not relivant.
28310  *
28311  * Fork - LGPL
28312  * <script type="text/javascript">
28313  */
28314
28315 /**
28316  * @class Roo.Resizable
28317  * @extends Roo.util.Observable
28318  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28319  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28320  * 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
28321  * the element will be wrapped for you automatically.</p>
28322  * <p>Here is the list of valid resize handles:</p>
28323  * <pre>
28324 Value   Description
28325 ------  -------------------
28326  'n'     north
28327  's'     south
28328  'e'     east
28329  'w'     west
28330  'nw'    northwest
28331  'sw'    southwest
28332  'se'    southeast
28333  'ne'    northeast
28334  'hd'    horizontal drag
28335  'all'   all
28336 </pre>
28337  * <p>Here's an example showing the creation of a typical Resizable:</p>
28338  * <pre><code>
28339 var resizer = new Roo.Resizable("element-id", {
28340     handles: 'all',
28341     minWidth: 200,
28342     minHeight: 100,
28343     maxWidth: 500,
28344     maxHeight: 400,
28345     pinned: true
28346 });
28347 resizer.on("resize", myHandler);
28348 </code></pre>
28349  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28350  * resizer.east.setDisplayed(false);</p>
28351  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28352  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28353  * resize operation's new size (defaults to [0, 0])
28354  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28355  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28356  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28357  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28358  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28359  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28360  * @cfg {Number} width The width of the element in pixels (defaults to null)
28361  * @cfg {Number} height The height of the element in pixels (defaults to null)
28362  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28363  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28364  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28365  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28366  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28367  * in favor of the handles config option (defaults to false)
28368  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28369  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28370  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28371  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28372  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28373  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28374  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28375  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28376  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28377  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28378  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28379  * @constructor
28380  * Create a new resizable component
28381  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
28382  * @param {Object} config configuration options
28383   */
28384 Roo.Resizable = function(el, config)
28385 {
28386     this.el = Roo.get(el);
28387
28388     if(config && config.wrap){
28389         config.resizeChild = this.el;
28390         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
28391         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
28392         this.el.setStyle("overflow", "hidden");
28393         this.el.setPositioning(config.resizeChild.getPositioning());
28394         config.resizeChild.clearPositioning();
28395         if(!config.width || !config.height){
28396             var csize = config.resizeChild.getSize();
28397             this.el.setSize(csize.width, csize.height);
28398         }
28399         if(config.pinned && !config.adjustments){
28400             config.adjustments = "auto";
28401         }
28402     }
28403
28404     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
28405     this.proxy.unselectable();
28406     this.proxy.enableDisplayMode('block');
28407
28408     Roo.apply(this, config);
28409
28410     if(this.pinned){
28411         this.disableTrackOver = true;
28412         this.el.addClass("x-resizable-pinned");
28413     }
28414     // if the element isn't positioned, make it relative
28415     var position = this.el.getStyle("position");
28416     if(position != "absolute" && position != "fixed"){
28417         this.el.setStyle("position", "relative");
28418     }
28419     if(!this.handles){ // no handles passed, must be legacy style
28420         this.handles = 's,e,se';
28421         if(this.multiDirectional){
28422             this.handles += ',n,w';
28423         }
28424     }
28425     if(this.handles == "all"){
28426         this.handles = "n s e w ne nw se sw";
28427     }
28428     var hs = this.handles.split(/\s*?[,;]\s*?| /);
28429     var ps = Roo.Resizable.positions;
28430     for(var i = 0, len = hs.length; i < len; i++){
28431         if(hs[i] && ps[hs[i]]){
28432             var pos = ps[hs[i]];
28433             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
28434         }
28435     }
28436     // legacy
28437     this.corner = this.southeast;
28438     
28439     // updateBox = the box can move..
28440     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
28441         this.updateBox = true;
28442     }
28443
28444     this.activeHandle = null;
28445
28446     if(this.resizeChild){
28447         if(typeof this.resizeChild == "boolean"){
28448             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
28449         }else{
28450             this.resizeChild = Roo.get(this.resizeChild, true);
28451         }
28452     }
28453     
28454     if(this.adjustments == "auto"){
28455         var rc = this.resizeChild;
28456         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
28457         if(rc && (hw || hn)){
28458             rc.position("relative");
28459             rc.setLeft(hw ? hw.el.getWidth() : 0);
28460             rc.setTop(hn ? hn.el.getHeight() : 0);
28461         }
28462         this.adjustments = [
28463             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
28464             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
28465         ];
28466     }
28467
28468     if(this.draggable){
28469         this.dd = this.dynamic ?
28470             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
28471         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
28472     }
28473
28474     // public events
28475     this.addEvents({
28476         /**
28477          * @event beforeresize
28478          * Fired before resize is allowed. Set enabled to false to cancel resize.
28479          * @param {Roo.Resizable} this
28480          * @param {Roo.EventObject} e The mousedown event
28481          */
28482         "beforeresize" : true,
28483         /**
28484          * @event resize
28485          * Fired after a resize.
28486          * @param {Roo.Resizable} this
28487          * @param {Number} width The new width
28488          * @param {Number} height The new height
28489          * @param {Roo.EventObject} e The mouseup event
28490          */
28491         "resize" : true
28492     });
28493
28494     if(this.width !== null && this.height !== null){
28495         this.resizeTo(this.width, this.height);
28496     }else{
28497         this.updateChildSize();
28498     }
28499     if(Roo.isIE){
28500         this.el.dom.style.zoom = 1;
28501     }
28502     Roo.Resizable.superclass.constructor.call(this);
28503 };
28504
28505 Roo.extend(Roo.Resizable, Roo.util.Observable, {
28506         resizeChild : false,
28507         adjustments : [0, 0],
28508         minWidth : 5,
28509         minHeight : 5,
28510         maxWidth : 10000,
28511         maxHeight : 10000,
28512         enabled : true,
28513         animate : false,
28514         duration : .35,
28515         dynamic : false,
28516         handles : false,
28517         multiDirectional : false,
28518         disableTrackOver : false,
28519         easing : 'easeOutStrong',
28520         widthIncrement : 0,
28521         heightIncrement : 0,
28522         pinned : false,
28523         width : null,
28524         height : null,
28525         preserveRatio : false,
28526         transparent: false,
28527         minX: 0,
28528         minY: 0,
28529         draggable: false,
28530
28531         /**
28532          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
28533          */
28534         constrainTo: undefined,
28535         /**
28536          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
28537          */
28538         resizeRegion: undefined,
28539
28540
28541     /**
28542      * Perform a manual resize
28543      * @param {Number} width
28544      * @param {Number} height
28545      */
28546     resizeTo : function(width, height){
28547         this.el.setSize(width, height);
28548         this.updateChildSize();
28549         this.fireEvent("resize", this, width, height, null);
28550     },
28551
28552     // private
28553     startSizing : function(e, handle){
28554         this.fireEvent("beforeresize", this, e);
28555         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
28556
28557             if(!this.overlay){
28558                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
28559                 this.overlay.unselectable();
28560                 this.overlay.enableDisplayMode("block");
28561                 this.overlay.on("mousemove", this.onMouseMove, this);
28562                 this.overlay.on("mouseup", this.onMouseUp, this);
28563             }
28564             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
28565
28566             this.resizing = true;
28567             this.startBox = this.el.getBox();
28568             this.startPoint = e.getXY();
28569             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
28570                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
28571
28572             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28573             this.overlay.show();
28574
28575             if(this.constrainTo) {
28576                 var ct = Roo.get(this.constrainTo);
28577                 this.resizeRegion = ct.getRegion().adjust(
28578                     ct.getFrameWidth('t'),
28579                     ct.getFrameWidth('l'),
28580                     -ct.getFrameWidth('b'),
28581                     -ct.getFrameWidth('r')
28582                 );
28583             }
28584
28585             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
28586             this.proxy.show();
28587             this.proxy.setBox(this.startBox);
28588             if(!this.dynamic){
28589                 this.proxy.setStyle('visibility', 'visible');
28590             }
28591         }
28592     },
28593
28594     // private
28595     onMouseDown : function(handle, e){
28596         if(this.enabled){
28597             e.stopEvent();
28598             this.activeHandle = handle;
28599             this.startSizing(e, handle);
28600         }
28601     },
28602
28603     // private
28604     onMouseUp : function(e){
28605         var size = this.resizeElement();
28606         this.resizing = false;
28607         this.handleOut();
28608         this.overlay.hide();
28609         this.proxy.hide();
28610         this.fireEvent("resize", this, size.width, size.height, e);
28611     },
28612
28613     // private
28614     updateChildSize : function(){
28615         if(this.resizeChild){
28616             var el = this.el;
28617             var child = this.resizeChild;
28618             var adj = this.adjustments;
28619             if(el.dom.offsetWidth){
28620                 var b = el.getSize(true);
28621                 child.setSize(b.width+adj[0], b.height+adj[1]);
28622             }
28623             // Second call here for IE
28624             // The first call enables instant resizing and
28625             // the second call corrects scroll bars if they
28626             // exist
28627             if(Roo.isIE){
28628                 setTimeout(function(){
28629                     if(el.dom.offsetWidth){
28630                         var b = el.getSize(true);
28631                         child.setSize(b.width+adj[0], b.height+adj[1]);
28632                     }
28633                 }, 10);
28634             }
28635         }
28636     },
28637
28638     // private
28639     snap : function(value, inc, min){
28640         if(!inc || !value) return value;
28641         var newValue = value;
28642         var m = value % inc;
28643         if(m > 0){
28644             if(m > (inc/2)){
28645                 newValue = value + (inc-m);
28646             }else{
28647                 newValue = value - m;
28648             }
28649         }
28650         return Math.max(min, newValue);
28651     },
28652
28653     // private
28654     resizeElement : function(){
28655         var box = this.proxy.getBox();
28656         if(this.updateBox){
28657             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28658         }else{
28659             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28660         }
28661         this.updateChildSize();
28662         if(!this.dynamic){
28663             this.proxy.hide();
28664         }
28665         return box;
28666     },
28667
28668     // private
28669     constrain : function(v, diff, m, mx){
28670         if(v - diff < m){
28671             diff = v - m;
28672         }else if(v - diff > mx){
28673             diff = mx - v;
28674         }
28675         return diff;
28676     },
28677
28678     // private
28679     onMouseMove : function(e){
28680         if(this.enabled){
28681             try{// try catch so if something goes wrong the user doesn't get hung
28682
28683             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28684                 return;
28685             }
28686
28687             //var curXY = this.startPoint;
28688             var curSize = this.curSize || this.startBox;
28689             var x = this.startBox.x, y = this.startBox.y;
28690             var ox = x, oy = y;
28691             var w = curSize.width, h = curSize.height;
28692             var ow = w, oh = h;
28693             var mw = this.minWidth, mh = this.minHeight;
28694             var mxw = this.maxWidth, mxh = this.maxHeight;
28695             var wi = this.widthIncrement;
28696             var hi = this.heightIncrement;
28697
28698             var eventXY = e.getXY();
28699             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28700             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28701
28702             var pos = this.activeHandle.position;
28703
28704             switch(pos){
28705                 case "east":
28706                     w += diffX;
28707                     w = Math.min(Math.max(mw, w), mxw);
28708                     break;
28709              
28710                 case "south":
28711                     h += diffY;
28712                     h = Math.min(Math.max(mh, h), mxh);
28713                     break;
28714                 case "southeast":
28715                     w += diffX;
28716                     h += diffY;
28717                     w = Math.min(Math.max(mw, w), mxw);
28718                     h = Math.min(Math.max(mh, h), mxh);
28719                     break;
28720                 case "north":
28721                     diffY = this.constrain(h, diffY, mh, mxh);
28722                     y += diffY;
28723                     h -= diffY;
28724                     break;
28725                 case "hdrag":
28726                     
28727                     if (wi) {
28728                         var adiffX = Math.abs(diffX);
28729                         var sub = (adiffX % wi); // how much 
28730                         if (sub > (wi/2)) { // far enough to snap
28731                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28732                         } else {
28733                             // remove difference.. 
28734                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28735                         }
28736                     }
28737                     x += diffX;
28738                     x = Math.max(this.minX, x);
28739                     break;
28740                 case "west":
28741                     diffX = this.constrain(w, diffX, mw, mxw);
28742                     x += diffX;
28743                     w -= diffX;
28744                     break;
28745                 case "northeast":
28746                     w += diffX;
28747                     w = Math.min(Math.max(mw, w), mxw);
28748                     diffY = this.constrain(h, diffY, mh, mxh);
28749                     y += diffY;
28750                     h -= diffY;
28751                     break;
28752                 case "northwest":
28753                     diffX = this.constrain(w, diffX, mw, mxw);
28754                     diffY = this.constrain(h, diffY, mh, mxh);
28755                     y += diffY;
28756                     h -= diffY;
28757                     x += diffX;
28758                     w -= diffX;
28759                     break;
28760                case "southwest":
28761                     diffX = this.constrain(w, diffX, mw, mxw);
28762                     h += diffY;
28763                     h = Math.min(Math.max(mh, h), mxh);
28764                     x += diffX;
28765                     w -= diffX;
28766                     break;
28767             }
28768
28769             var sw = this.snap(w, wi, mw);
28770             var sh = this.snap(h, hi, mh);
28771             if(sw != w || sh != h){
28772                 switch(pos){
28773                     case "northeast":
28774                         y -= sh - h;
28775                     break;
28776                     case "north":
28777                         y -= sh - h;
28778                         break;
28779                     case "southwest":
28780                         x -= sw - w;
28781                     break;
28782                     case "west":
28783                         x -= sw - w;
28784                         break;
28785                     case "northwest":
28786                         x -= sw - w;
28787                         y -= sh - h;
28788                     break;
28789                 }
28790                 w = sw;
28791                 h = sh;
28792             }
28793
28794             if(this.preserveRatio){
28795                 switch(pos){
28796                     case "southeast":
28797                     case "east":
28798                         h = oh * (w/ow);
28799                         h = Math.min(Math.max(mh, h), mxh);
28800                         w = ow * (h/oh);
28801                        break;
28802                     case "south":
28803                         w = ow * (h/oh);
28804                         w = Math.min(Math.max(mw, w), mxw);
28805                         h = oh * (w/ow);
28806                         break;
28807                     case "northeast":
28808                         w = ow * (h/oh);
28809                         w = Math.min(Math.max(mw, w), mxw);
28810                         h = oh * (w/ow);
28811                     break;
28812                     case "north":
28813                         var tw = w;
28814                         w = ow * (h/oh);
28815                         w = Math.min(Math.max(mw, w), mxw);
28816                         h = oh * (w/ow);
28817                         x += (tw - w) / 2;
28818                         break;
28819                     case "southwest":
28820                         h = oh * (w/ow);
28821                         h = Math.min(Math.max(mh, h), mxh);
28822                         var tw = w;
28823                         w = ow * (h/oh);
28824                         x += tw - w;
28825                         break;
28826                     case "west":
28827                         var th = h;
28828                         h = oh * (w/ow);
28829                         h = Math.min(Math.max(mh, h), mxh);
28830                         y += (th - h) / 2;
28831                         var tw = w;
28832                         w = ow * (h/oh);
28833                         x += tw - w;
28834                        break;
28835                     case "northwest":
28836                         var tw = w;
28837                         var th = h;
28838                         h = oh * (w/ow);
28839                         h = Math.min(Math.max(mh, h), mxh);
28840                         w = ow * (h/oh);
28841                         y += th - h;
28842                         x += tw - w;
28843                        break;
28844
28845                 }
28846             }
28847             if (pos == 'hdrag') {
28848                 w = ow;
28849             }
28850             this.proxy.setBounds(x, y, w, h);
28851             if(this.dynamic){
28852                 this.resizeElement();
28853             }
28854             }catch(e){}
28855         }
28856     },
28857
28858     // private
28859     handleOver : function(){
28860         if(this.enabled){
28861             this.el.addClass("x-resizable-over");
28862         }
28863     },
28864
28865     // private
28866     handleOut : function(){
28867         if(!this.resizing){
28868             this.el.removeClass("x-resizable-over");
28869         }
28870     },
28871
28872     /**
28873      * Returns the element this component is bound to.
28874      * @return {Roo.Element}
28875      */
28876     getEl : function(){
28877         return this.el;
28878     },
28879
28880     /**
28881      * Returns the resizeChild element (or null).
28882      * @return {Roo.Element}
28883      */
28884     getResizeChild : function(){
28885         return this.resizeChild;
28886     },
28887
28888     /**
28889      * Destroys this resizable. If the element was wrapped and
28890      * removeEl is not true then the element remains.
28891      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28892      */
28893     destroy : function(removeEl){
28894         this.proxy.remove();
28895         if(this.overlay){
28896             this.overlay.removeAllListeners();
28897             this.overlay.remove();
28898         }
28899         var ps = Roo.Resizable.positions;
28900         for(var k in ps){
28901             if(typeof ps[k] != "function" && this[ps[k]]){
28902                 var h = this[ps[k]];
28903                 h.el.removeAllListeners();
28904                 h.el.remove();
28905             }
28906         }
28907         if(removeEl){
28908             this.el.update("");
28909             this.el.remove();
28910         }
28911     }
28912 });
28913
28914 // private
28915 // hash to map config positions to true positions
28916 Roo.Resizable.positions = {
28917     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28918     hd: "hdrag"
28919 };
28920
28921 // private
28922 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28923     if(!this.tpl){
28924         // only initialize the template if resizable is used
28925         var tpl = Roo.DomHelper.createTemplate(
28926             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28927         );
28928         tpl.compile();
28929         Roo.Resizable.Handle.prototype.tpl = tpl;
28930     }
28931     this.position = pos;
28932     this.rz = rz;
28933     // show north drag fro topdra
28934     var handlepos = pos == 'hdrag' ? 'north' : pos;
28935     
28936     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28937     if (pos == 'hdrag') {
28938         this.el.setStyle('cursor', 'pointer');
28939     }
28940     this.el.unselectable();
28941     if(transparent){
28942         this.el.setOpacity(0);
28943     }
28944     this.el.on("mousedown", this.onMouseDown, this);
28945     if(!disableTrackOver){
28946         this.el.on("mouseover", this.onMouseOver, this);
28947         this.el.on("mouseout", this.onMouseOut, this);
28948     }
28949 };
28950
28951 // private
28952 Roo.Resizable.Handle.prototype = {
28953     afterResize : function(rz){
28954         // do nothing
28955     },
28956     // private
28957     onMouseDown : function(e){
28958         this.rz.onMouseDown(this, e);
28959     },
28960     // private
28961     onMouseOver : function(e){
28962         this.rz.handleOver(this, e);
28963     },
28964     // private
28965     onMouseOut : function(e){
28966         this.rz.handleOut(this, e);
28967     }
28968 };/*
28969  * Based on:
28970  * Ext JS Library 1.1.1
28971  * Copyright(c) 2006-2007, Ext JS, LLC.
28972  *
28973  * Originally Released Under LGPL - original licence link has changed is not relivant.
28974  *
28975  * Fork - LGPL
28976  * <script type="text/javascript">
28977  */
28978
28979 /**
28980  * @class Roo.Editor
28981  * @extends Roo.Component
28982  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28983  * @constructor
28984  * Create a new Editor
28985  * @param {Roo.form.Field} field The Field object (or descendant)
28986  * @param {Object} config The config object
28987  */
28988 Roo.Editor = function(field, config){
28989     Roo.Editor.superclass.constructor.call(this, config);
28990     this.field = field;
28991     this.addEvents({
28992         /**
28993              * @event beforestartedit
28994              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28995              * false from the handler of this event.
28996              * @param {Editor} this
28997              * @param {Roo.Element} boundEl The underlying element bound to this editor
28998              * @param {Mixed} value The field value being set
28999              */
29000         "beforestartedit" : true,
29001         /**
29002              * @event startedit
29003              * Fires when this editor is displayed
29004              * @param {Roo.Element} boundEl The underlying element bound to this editor
29005              * @param {Mixed} value The starting field value
29006              */
29007         "startedit" : true,
29008         /**
29009              * @event beforecomplete
29010              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29011              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29012              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29013              * event will not fire since no edit actually occurred.
29014              * @param {Editor} this
29015              * @param {Mixed} value The current field value
29016              * @param {Mixed} startValue The original field value
29017              */
29018         "beforecomplete" : true,
29019         /**
29020              * @event complete
29021              * Fires after editing is complete and any changed value has been written to the underlying field.
29022              * @param {Editor} this
29023              * @param {Mixed} value The current field value
29024              * @param {Mixed} startValue The original field value
29025              */
29026         "complete" : true,
29027         /**
29028          * @event specialkey
29029          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29030          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29031          * @param {Roo.form.Field} this
29032          * @param {Roo.EventObject} e The event object
29033          */
29034         "specialkey" : true
29035     });
29036 };
29037
29038 Roo.extend(Roo.Editor, Roo.Component, {
29039     /**
29040      * @cfg {Boolean/String} autosize
29041      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29042      * or "height" to adopt the height only (defaults to false)
29043      */
29044     /**
29045      * @cfg {Boolean} revertInvalid
29046      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29047      * validation fails (defaults to true)
29048      */
29049     /**
29050      * @cfg {Boolean} ignoreNoChange
29051      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29052      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29053      * will never be ignored.
29054      */
29055     /**
29056      * @cfg {Boolean} hideEl
29057      * False to keep the bound element visible while the editor is displayed (defaults to true)
29058      */
29059     /**
29060      * @cfg {Mixed} value
29061      * The data value of the underlying field (defaults to "")
29062      */
29063     value : "",
29064     /**
29065      * @cfg {String} alignment
29066      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29067      */
29068     alignment: "c-c?",
29069     /**
29070      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29071      * for bottom-right shadow (defaults to "frame")
29072      */
29073     shadow : "frame",
29074     /**
29075      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29076      */
29077     constrain : false,
29078     /**
29079      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29080      */
29081     completeOnEnter : false,
29082     /**
29083      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29084      */
29085     cancelOnEsc : false,
29086     /**
29087      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29088      */
29089     updateEl : false,
29090
29091     // private
29092     onRender : function(ct, position){
29093         this.el = new Roo.Layer({
29094             shadow: this.shadow,
29095             cls: "x-editor",
29096             parentEl : ct,
29097             shim : this.shim,
29098             shadowOffset:4,
29099             id: this.id,
29100             constrain: this.constrain
29101         });
29102         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29103         if(this.field.msgTarget != 'title'){
29104             this.field.msgTarget = 'qtip';
29105         }
29106         this.field.render(this.el);
29107         if(Roo.isGecko){
29108             this.field.el.dom.setAttribute('autocomplete', 'off');
29109         }
29110         this.field.on("specialkey", this.onSpecialKey, this);
29111         if(this.swallowKeys){
29112             this.field.el.swallowEvent(['keydown','keypress']);
29113         }
29114         this.field.show();
29115         this.field.on("blur", this.onBlur, this);
29116         if(this.field.grow){
29117             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29118         }
29119     },
29120
29121     onSpecialKey : function(field, e)
29122     {
29123         //Roo.log('editor onSpecialKey');
29124         if(this.completeOnEnter && e.getKey() == e.ENTER){
29125             e.stopEvent();
29126             this.completeEdit();
29127             return;
29128         }
29129         // do not fire special key otherwise it might hide close the editor...
29130         if(e.getKey() == e.ENTER){    
29131             return;
29132         }
29133         if(this.cancelOnEsc && e.getKey() == e.ESC){
29134             this.cancelEdit();
29135             return;
29136         } 
29137         this.fireEvent('specialkey', field, e);
29138     
29139     },
29140
29141     /**
29142      * Starts the editing process and shows the editor.
29143      * @param {String/HTMLElement/Element} el The element to edit
29144      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29145       * to the innerHTML of el.
29146      */
29147     startEdit : function(el, value){
29148         if(this.editing){
29149             this.completeEdit();
29150         }
29151         this.boundEl = Roo.get(el);
29152         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29153         if(!this.rendered){
29154             this.render(this.parentEl || document.body);
29155         }
29156         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29157             return;
29158         }
29159         this.startValue = v;
29160         this.field.setValue(v);
29161         if(this.autoSize){
29162             var sz = this.boundEl.getSize();
29163             switch(this.autoSize){
29164                 case "width":
29165                 this.setSize(sz.width,  "");
29166                 break;
29167                 case "height":
29168                 this.setSize("",  sz.height);
29169                 break;
29170                 default:
29171                 this.setSize(sz.width,  sz.height);
29172             }
29173         }
29174         this.el.alignTo(this.boundEl, this.alignment);
29175         this.editing = true;
29176         if(Roo.QuickTips){
29177             Roo.QuickTips.disable();
29178         }
29179         this.show();
29180     },
29181
29182     /**
29183      * Sets the height and width of this editor.
29184      * @param {Number} width The new width
29185      * @param {Number} height The new height
29186      */
29187     setSize : function(w, h){
29188         this.field.setSize(w, h);
29189         if(this.el){
29190             this.el.sync();
29191         }
29192     },
29193
29194     /**
29195      * Realigns the editor to the bound field based on the current alignment config value.
29196      */
29197     realign : function(){
29198         this.el.alignTo(this.boundEl, this.alignment);
29199     },
29200
29201     /**
29202      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29203      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29204      */
29205     completeEdit : function(remainVisible){
29206         if(!this.editing){
29207             return;
29208         }
29209         var v = this.getValue();
29210         if(this.revertInvalid !== false && !this.field.isValid()){
29211             v = this.startValue;
29212             this.cancelEdit(true);
29213         }
29214         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29215             this.editing = false;
29216             this.hide();
29217             return;
29218         }
29219         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29220             this.editing = false;
29221             if(this.updateEl && this.boundEl){
29222                 this.boundEl.update(v);
29223             }
29224             if(remainVisible !== true){
29225                 this.hide();
29226             }
29227             this.fireEvent("complete", this, v, this.startValue);
29228         }
29229     },
29230
29231     // private
29232     onShow : function(){
29233         this.el.show();
29234         if(this.hideEl !== false){
29235             this.boundEl.hide();
29236         }
29237         this.field.show();
29238         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29239             this.fixIEFocus = true;
29240             this.deferredFocus.defer(50, this);
29241         }else{
29242             this.field.focus();
29243         }
29244         this.fireEvent("startedit", this.boundEl, this.startValue);
29245     },
29246
29247     deferredFocus : function(){
29248         if(this.editing){
29249             this.field.focus();
29250         }
29251     },
29252
29253     /**
29254      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29255      * reverted to the original starting value.
29256      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29257      * cancel (defaults to false)
29258      */
29259     cancelEdit : function(remainVisible){
29260         if(this.editing){
29261             this.setValue(this.startValue);
29262             if(remainVisible !== true){
29263                 this.hide();
29264             }
29265         }
29266     },
29267
29268     // private
29269     onBlur : function(){
29270         if(this.allowBlur !== true && this.editing){
29271             this.completeEdit();
29272         }
29273     },
29274
29275     // private
29276     onHide : function(){
29277         if(this.editing){
29278             this.completeEdit();
29279             return;
29280         }
29281         this.field.blur();
29282         if(this.field.collapse){
29283             this.field.collapse();
29284         }
29285         this.el.hide();
29286         if(this.hideEl !== false){
29287             this.boundEl.show();
29288         }
29289         if(Roo.QuickTips){
29290             Roo.QuickTips.enable();
29291         }
29292     },
29293
29294     /**
29295      * Sets the data value of the editor
29296      * @param {Mixed} value Any valid value supported by the underlying field
29297      */
29298     setValue : function(v){
29299         this.field.setValue(v);
29300     },
29301
29302     /**
29303      * Gets the data value of the editor
29304      * @return {Mixed} The data value
29305      */
29306     getValue : function(){
29307         return this.field.getValue();
29308     }
29309 });/*
29310  * Based on:
29311  * Ext JS Library 1.1.1
29312  * Copyright(c) 2006-2007, Ext JS, LLC.
29313  *
29314  * Originally Released Under LGPL - original licence link has changed is not relivant.
29315  *
29316  * Fork - LGPL
29317  * <script type="text/javascript">
29318  */
29319  
29320 /**
29321  * @class Roo.BasicDialog
29322  * @extends Roo.util.Observable
29323  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29324  * <pre><code>
29325 var dlg = new Roo.BasicDialog("my-dlg", {
29326     height: 200,
29327     width: 300,
29328     minHeight: 100,
29329     minWidth: 150,
29330     modal: true,
29331     proxyDrag: true,
29332     shadow: true
29333 });
29334 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29335 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29336 dlg.addButton('Cancel', dlg.hide, dlg);
29337 dlg.show();
29338 </code></pre>
29339   <b>A Dialog should always be a direct child of the body element.</b>
29340  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29341  * @cfg {String} title Default text to display in the title bar (defaults to null)
29342  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29343  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29344  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29345  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29346  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29347  * (defaults to null with no animation)
29348  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29349  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29350  * property for valid values (defaults to 'all')
29351  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29352  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29353  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29354  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29355  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29356  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29357  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29358  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29359  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29360  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29361  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29362  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
29363  * draggable = true (defaults to false)
29364  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
29365  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29366  * shadow (defaults to false)
29367  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
29368  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
29369  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
29370  * @cfg {Array} buttons Array of buttons
29371  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
29372  * @constructor
29373  * Create a new BasicDialog.
29374  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
29375  * @param {Object} config Configuration options
29376  */
29377 Roo.BasicDialog = function(el, config){
29378     this.el = Roo.get(el);
29379     var dh = Roo.DomHelper;
29380     if(!this.el && config && config.autoCreate){
29381         if(typeof config.autoCreate == "object"){
29382             if(!config.autoCreate.id){
29383                 config.autoCreate.id = el;
29384             }
29385             this.el = dh.append(document.body,
29386                         config.autoCreate, true);
29387         }else{
29388             this.el = dh.append(document.body,
29389                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
29390         }
29391     }
29392     el = this.el;
29393     el.setDisplayed(true);
29394     el.hide = this.hideAction;
29395     this.id = el.id;
29396     el.addClass("x-dlg");
29397
29398     Roo.apply(this, config);
29399
29400     this.proxy = el.createProxy("x-dlg-proxy");
29401     this.proxy.hide = this.hideAction;
29402     this.proxy.setOpacity(.5);
29403     this.proxy.hide();
29404
29405     if(config.width){
29406         el.setWidth(config.width);
29407     }
29408     if(config.height){
29409         el.setHeight(config.height);
29410     }
29411     this.size = el.getSize();
29412     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
29413         this.xy = [config.x,config.y];
29414     }else{
29415         this.xy = el.getCenterXY(true);
29416     }
29417     /** The header element @type Roo.Element */
29418     this.header = el.child("> .x-dlg-hd");
29419     /** The body element @type Roo.Element */
29420     this.body = el.child("> .x-dlg-bd");
29421     /** The footer element @type Roo.Element */
29422     this.footer = el.child("> .x-dlg-ft");
29423
29424     if(!this.header){
29425         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
29426     }
29427     if(!this.body){
29428         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
29429     }
29430
29431     this.header.unselectable();
29432     if(this.title){
29433         this.header.update(this.title);
29434     }
29435     // this element allows the dialog to be focused for keyboard event
29436     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
29437     this.focusEl.swallowEvent("click", true);
29438
29439     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
29440
29441     // wrap the body and footer for special rendering
29442     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
29443     if(this.footer){
29444         this.bwrap.dom.appendChild(this.footer.dom);
29445     }
29446
29447     this.bg = this.el.createChild({
29448         tag: "div", cls:"x-dlg-bg",
29449         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
29450     });
29451     this.centerBg = this.bg.child("div.x-dlg-bg-center");
29452
29453
29454     if(this.autoScroll !== false && !this.autoTabs){
29455         this.body.setStyle("overflow", "auto");
29456     }
29457
29458     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
29459
29460     if(this.closable !== false){
29461         this.el.addClass("x-dlg-closable");
29462         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
29463         this.close.on("click", this.closeClick, this);
29464         this.close.addClassOnOver("x-dlg-close-over");
29465     }
29466     if(this.collapsible !== false){
29467         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
29468         this.collapseBtn.on("click", this.collapseClick, this);
29469         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
29470         this.header.on("dblclick", this.collapseClick, this);
29471     }
29472     if(this.resizable !== false){
29473         this.el.addClass("x-dlg-resizable");
29474         this.resizer = new Roo.Resizable(el, {
29475             minWidth: this.minWidth || 80,
29476             minHeight:this.minHeight || 80,
29477             handles: this.resizeHandles || "all",
29478             pinned: true
29479         });
29480         this.resizer.on("beforeresize", this.beforeResize, this);
29481         this.resizer.on("resize", this.onResize, this);
29482     }
29483     if(this.draggable !== false){
29484         el.addClass("x-dlg-draggable");
29485         if (!this.proxyDrag) {
29486             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
29487         }
29488         else {
29489             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
29490         }
29491         dd.setHandleElId(this.header.id);
29492         dd.endDrag = this.endMove.createDelegate(this);
29493         dd.startDrag = this.startMove.createDelegate(this);
29494         dd.onDrag = this.onDrag.createDelegate(this);
29495         dd.scroll = false;
29496         this.dd = dd;
29497     }
29498     if(this.modal){
29499         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
29500         this.mask.enableDisplayMode("block");
29501         this.mask.hide();
29502         this.el.addClass("x-dlg-modal");
29503     }
29504     if(this.shadow){
29505         this.shadow = new Roo.Shadow({
29506             mode : typeof this.shadow == "string" ? this.shadow : "sides",
29507             offset : this.shadowOffset
29508         });
29509     }else{
29510         this.shadowOffset = 0;
29511     }
29512     if(Roo.useShims && this.shim !== false){
29513         this.shim = this.el.createShim();
29514         this.shim.hide = this.hideAction;
29515         this.shim.hide();
29516     }else{
29517         this.shim = false;
29518     }
29519     if(this.autoTabs){
29520         this.initTabs();
29521     }
29522     if (this.buttons) { 
29523         var bts= this.buttons;
29524         this.buttons = [];
29525         Roo.each(bts, function(b) {
29526             this.addButton(b);
29527         }, this);
29528     }
29529     
29530     
29531     this.addEvents({
29532         /**
29533          * @event keydown
29534          * Fires when a key is pressed
29535          * @param {Roo.BasicDialog} this
29536          * @param {Roo.EventObject} e
29537          */
29538         "keydown" : true,
29539         /**
29540          * @event move
29541          * Fires when this dialog is moved by the user.
29542          * @param {Roo.BasicDialog} this
29543          * @param {Number} x The new page X
29544          * @param {Number} y The new page Y
29545          */
29546         "move" : true,
29547         /**
29548          * @event resize
29549          * Fires when this dialog is resized by the user.
29550          * @param {Roo.BasicDialog} this
29551          * @param {Number} width The new width
29552          * @param {Number} height The new height
29553          */
29554         "resize" : true,
29555         /**
29556          * @event beforehide
29557          * Fires before this dialog is hidden.
29558          * @param {Roo.BasicDialog} this
29559          */
29560         "beforehide" : true,
29561         /**
29562          * @event hide
29563          * Fires when this dialog is hidden.
29564          * @param {Roo.BasicDialog} this
29565          */
29566         "hide" : true,
29567         /**
29568          * @event beforeshow
29569          * Fires before this dialog is shown.
29570          * @param {Roo.BasicDialog} this
29571          */
29572         "beforeshow" : true,
29573         /**
29574          * @event show
29575          * Fires when this dialog is shown.
29576          * @param {Roo.BasicDialog} this
29577          */
29578         "show" : true
29579     });
29580     el.on("keydown", this.onKeyDown, this);
29581     el.on("mousedown", this.toFront, this);
29582     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
29583     this.el.hide();
29584     Roo.DialogManager.register(this);
29585     Roo.BasicDialog.superclass.constructor.call(this);
29586 };
29587
29588 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29589     shadowOffset: Roo.isIE ? 6 : 5,
29590     minHeight: 80,
29591     minWidth: 200,
29592     minButtonWidth: 75,
29593     defaultButton: null,
29594     buttonAlign: "right",
29595     tabTag: 'div',
29596     firstShow: true,
29597
29598     /**
29599      * Sets the dialog title text
29600      * @param {String} text The title text to display
29601      * @return {Roo.BasicDialog} this
29602      */
29603     setTitle : function(text){
29604         this.header.update(text);
29605         return this;
29606     },
29607
29608     // private
29609     closeClick : function(){
29610         this.hide();
29611     },
29612
29613     // private
29614     collapseClick : function(){
29615         this[this.collapsed ? "expand" : "collapse"]();
29616     },
29617
29618     /**
29619      * Collapses the dialog to its minimized state (only the title bar is visible).
29620      * Equivalent to the user clicking the collapse dialog button.
29621      */
29622     collapse : function(){
29623         if(!this.collapsed){
29624             this.collapsed = true;
29625             this.el.addClass("x-dlg-collapsed");
29626             this.restoreHeight = this.el.getHeight();
29627             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29628         }
29629     },
29630
29631     /**
29632      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29633      * clicking the expand dialog button.
29634      */
29635     expand : function(){
29636         if(this.collapsed){
29637             this.collapsed = false;
29638             this.el.removeClass("x-dlg-collapsed");
29639             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29640         }
29641     },
29642
29643     /**
29644      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29645      * @return {Roo.TabPanel} The tabs component
29646      */
29647     initTabs : function(){
29648         var tabs = this.getTabs();
29649         while(tabs.getTab(0)){
29650             tabs.removeTab(0);
29651         }
29652         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29653             var dom = el.dom;
29654             tabs.addTab(Roo.id(dom), dom.title);
29655             dom.title = "";
29656         });
29657         tabs.activate(0);
29658         return tabs;
29659     },
29660
29661     // private
29662     beforeResize : function(){
29663         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29664     },
29665
29666     // private
29667     onResize : function(){
29668         this.refreshSize();
29669         this.syncBodyHeight();
29670         this.adjustAssets();
29671         this.focus();
29672         this.fireEvent("resize", this, this.size.width, this.size.height);
29673     },
29674
29675     // private
29676     onKeyDown : function(e){
29677         if(this.isVisible()){
29678             this.fireEvent("keydown", this, e);
29679         }
29680     },
29681
29682     /**
29683      * Resizes the dialog.
29684      * @param {Number} width
29685      * @param {Number} height
29686      * @return {Roo.BasicDialog} this
29687      */
29688     resizeTo : function(width, height){
29689         this.el.setSize(width, height);
29690         this.size = {width: width, height: height};
29691         this.syncBodyHeight();
29692         if(this.fixedcenter){
29693             this.center();
29694         }
29695         if(this.isVisible()){
29696             this.constrainXY();
29697             this.adjustAssets();
29698         }
29699         this.fireEvent("resize", this, width, height);
29700         return this;
29701     },
29702
29703
29704     /**
29705      * Resizes the dialog to fit the specified content size.
29706      * @param {Number} width
29707      * @param {Number} height
29708      * @return {Roo.BasicDialog} this
29709      */
29710     setContentSize : function(w, h){
29711         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29712         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29713         //if(!this.el.isBorderBox()){
29714             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29715             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29716         //}
29717         if(this.tabs){
29718             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29719             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29720         }
29721         this.resizeTo(w, h);
29722         return this;
29723     },
29724
29725     /**
29726      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29727      * executed in response to a particular key being pressed while the dialog is active.
29728      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29729      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29730      * @param {Function} fn The function to call
29731      * @param {Object} scope (optional) The scope of the function
29732      * @return {Roo.BasicDialog} this
29733      */
29734     addKeyListener : function(key, fn, scope){
29735         var keyCode, shift, ctrl, alt;
29736         if(typeof key == "object" && !(key instanceof Array)){
29737             keyCode = key["key"];
29738             shift = key["shift"];
29739             ctrl = key["ctrl"];
29740             alt = key["alt"];
29741         }else{
29742             keyCode = key;
29743         }
29744         var handler = function(dlg, e){
29745             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29746                 var k = e.getKey();
29747                 if(keyCode instanceof Array){
29748                     for(var i = 0, len = keyCode.length; i < len; i++){
29749                         if(keyCode[i] == k){
29750                           fn.call(scope || window, dlg, k, e);
29751                           return;
29752                         }
29753                     }
29754                 }else{
29755                     if(k == keyCode){
29756                         fn.call(scope || window, dlg, k, e);
29757                     }
29758                 }
29759             }
29760         };
29761         this.on("keydown", handler);
29762         return this;
29763     },
29764
29765     /**
29766      * Returns the TabPanel component (creates it if it doesn't exist).
29767      * Note: If you wish to simply check for the existence of tabs without creating them,
29768      * check for a null 'tabs' property.
29769      * @return {Roo.TabPanel} The tabs component
29770      */
29771     getTabs : function(){
29772         if(!this.tabs){
29773             this.el.addClass("x-dlg-auto-tabs");
29774             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29775             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29776         }
29777         return this.tabs;
29778     },
29779
29780     /**
29781      * Adds a button to the footer section of the dialog.
29782      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29783      * object or a valid Roo.DomHelper element config
29784      * @param {Function} handler The function called when the button is clicked
29785      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29786      * @return {Roo.Button} The new button
29787      */
29788     addButton : function(config, handler, scope){
29789         var dh = Roo.DomHelper;
29790         if(!this.footer){
29791             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29792         }
29793         if(!this.btnContainer){
29794             var tb = this.footer.createChild({
29795
29796                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29797                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29798             }, null, true);
29799             this.btnContainer = tb.firstChild.firstChild.firstChild;
29800         }
29801         var bconfig = {
29802             handler: handler,
29803             scope: scope,
29804             minWidth: this.minButtonWidth,
29805             hideParent:true
29806         };
29807         if(typeof config == "string"){
29808             bconfig.text = config;
29809         }else{
29810             if(config.tag){
29811                 bconfig.dhconfig = config;
29812             }else{
29813                 Roo.apply(bconfig, config);
29814             }
29815         }
29816         var fc = false;
29817         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29818             bconfig.position = Math.max(0, bconfig.position);
29819             fc = this.btnContainer.childNodes[bconfig.position];
29820         }
29821          
29822         var btn = new Roo.Button(
29823             fc ? 
29824                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29825                 : this.btnContainer.appendChild(document.createElement("td")),
29826             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29827             bconfig
29828         );
29829         this.syncBodyHeight();
29830         if(!this.buttons){
29831             /**
29832              * Array of all the buttons that have been added to this dialog via addButton
29833              * @type Array
29834              */
29835             this.buttons = [];
29836         }
29837         this.buttons.push(btn);
29838         return btn;
29839     },
29840
29841     /**
29842      * Sets the default button to be focused when the dialog is displayed.
29843      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29844      * @return {Roo.BasicDialog} this
29845      */
29846     setDefaultButton : function(btn){
29847         this.defaultButton = btn;
29848         return this;
29849     },
29850
29851     // private
29852     getHeaderFooterHeight : function(safe){
29853         var height = 0;
29854         if(this.header){
29855            height += this.header.getHeight();
29856         }
29857         if(this.footer){
29858            var fm = this.footer.getMargins();
29859             height += (this.footer.getHeight()+fm.top+fm.bottom);
29860         }
29861         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29862         height += this.centerBg.getPadding("tb");
29863         return height;
29864     },
29865
29866     // private
29867     syncBodyHeight : function(){
29868         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29869         var height = this.size.height - this.getHeaderFooterHeight(false);
29870         bd.setHeight(height-bd.getMargins("tb"));
29871         var hh = this.header.getHeight();
29872         var h = this.size.height-hh;
29873         cb.setHeight(h);
29874         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29875         bw.setHeight(h-cb.getPadding("tb"));
29876         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29877         bd.setWidth(bw.getWidth(true));
29878         if(this.tabs){
29879             this.tabs.syncHeight();
29880             if(Roo.isIE){
29881                 this.tabs.el.repaint();
29882             }
29883         }
29884     },
29885
29886     /**
29887      * Restores the previous state of the dialog if Roo.state is configured.
29888      * @return {Roo.BasicDialog} this
29889      */
29890     restoreState : function(){
29891         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29892         if(box && box.width){
29893             this.xy = [box.x, box.y];
29894             this.resizeTo(box.width, box.height);
29895         }
29896         return this;
29897     },
29898
29899     // private
29900     beforeShow : function(){
29901         this.expand();
29902         if(this.fixedcenter){
29903             this.xy = this.el.getCenterXY(true);
29904         }
29905         if(this.modal){
29906             Roo.get(document.body).addClass("x-body-masked");
29907             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29908             this.mask.show();
29909         }
29910         this.constrainXY();
29911     },
29912
29913     // private
29914     animShow : function(){
29915         var b = Roo.get(this.animateTarget).getBox();
29916         this.proxy.setSize(b.width, b.height);
29917         this.proxy.setLocation(b.x, b.y);
29918         this.proxy.show();
29919         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29920                     true, .35, this.showEl.createDelegate(this));
29921     },
29922
29923     /**
29924      * Shows the dialog.
29925      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29926      * @return {Roo.BasicDialog} this
29927      */
29928     show : function(animateTarget){
29929         if (this.fireEvent("beforeshow", this) === false){
29930             return;
29931         }
29932         if(this.syncHeightBeforeShow){
29933             this.syncBodyHeight();
29934         }else if(this.firstShow){
29935             this.firstShow = false;
29936             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29937         }
29938         this.animateTarget = animateTarget || this.animateTarget;
29939         if(!this.el.isVisible()){
29940             this.beforeShow();
29941             if(this.animateTarget && Roo.get(this.animateTarget)){
29942                 this.animShow();
29943             }else{
29944                 this.showEl();
29945             }
29946         }
29947         return this;
29948     },
29949
29950     // private
29951     showEl : function(){
29952         this.proxy.hide();
29953         this.el.setXY(this.xy);
29954         this.el.show();
29955         this.adjustAssets(true);
29956         this.toFront();
29957         this.focus();
29958         // IE peekaboo bug - fix found by Dave Fenwick
29959         if(Roo.isIE){
29960             this.el.repaint();
29961         }
29962         this.fireEvent("show", this);
29963     },
29964
29965     /**
29966      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29967      * dialog itself will receive focus.
29968      */
29969     focus : function(){
29970         if(this.defaultButton){
29971             this.defaultButton.focus();
29972         }else{
29973             this.focusEl.focus();
29974         }
29975     },
29976
29977     // private
29978     constrainXY : function(){
29979         if(this.constraintoviewport !== false){
29980             if(!this.viewSize){
29981                 if(this.container){
29982                     var s = this.container.getSize();
29983                     this.viewSize = [s.width, s.height];
29984                 }else{
29985                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29986                 }
29987             }
29988             var s = Roo.get(this.container||document).getScroll();
29989
29990             var x = this.xy[0], y = this.xy[1];
29991             var w = this.size.width, h = this.size.height;
29992             var vw = this.viewSize[0], vh = this.viewSize[1];
29993             // only move it if it needs it
29994             var moved = false;
29995             // first validate right/bottom
29996             if(x + w > vw+s.left){
29997                 x = vw - w;
29998                 moved = true;
29999             }
30000             if(y + h > vh+s.top){
30001                 y = vh - h;
30002                 moved = true;
30003             }
30004             // then make sure top/left isn't negative
30005             if(x < s.left){
30006                 x = s.left;
30007                 moved = true;
30008             }
30009             if(y < s.top){
30010                 y = s.top;
30011                 moved = true;
30012             }
30013             if(moved){
30014                 // cache xy
30015                 this.xy = [x, y];
30016                 if(this.isVisible()){
30017                     this.el.setLocation(x, y);
30018                     this.adjustAssets();
30019                 }
30020             }
30021         }
30022     },
30023
30024     // private
30025     onDrag : function(){
30026         if(!this.proxyDrag){
30027             this.xy = this.el.getXY();
30028             this.adjustAssets();
30029         }
30030     },
30031
30032     // private
30033     adjustAssets : function(doShow){
30034         var x = this.xy[0], y = this.xy[1];
30035         var w = this.size.width, h = this.size.height;
30036         if(doShow === true){
30037             if(this.shadow){
30038                 this.shadow.show(this.el);
30039             }
30040             if(this.shim){
30041                 this.shim.show();
30042             }
30043         }
30044         if(this.shadow && this.shadow.isVisible()){
30045             this.shadow.show(this.el);
30046         }
30047         if(this.shim && this.shim.isVisible()){
30048             this.shim.setBounds(x, y, w, h);
30049         }
30050     },
30051
30052     // private
30053     adjustViewport : function(w, h){
30054         if(!w || !h){
30055             w = Roo.lib.Dom.getViewWidth();
30056             h = Roo.lib.Dom.getViewHeight();
30057         }
30058         // cache the size
30059         this.viewSize = [w, h];
30060         if(this.modal && this.mask.isVisible()){
30061             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30062             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30063         }
30064         if(this.isVisible()){
30065             this.constrainXY();
30066         }
30067     },
30068
30069     /**
30070      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30071      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30072      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30073      */
30074     destroy : function(removeEl){
30075         if(this.isVisible()){
30076             this.animateTarget = null;
30077             this.hide();
30078         }
30079         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30080         if(this.tabs){
30081             this.tabs.destroy(removeEl);
30082         }
30083         Roo.destroy(
30084              this.shim,
30085              this.proxy,
30086              this.resizer,
30087              this.close,
30088              this.mask
30089         );
30090         if(this.dd){
30091             this.dd.unreg();
30092         }
30093         if(this.buttons){
30094            for(var i = 0, len = this.buttons.length; i < len; i++){
30095                this.buttons[i].destroy();
30096            }
30097         }
30098         this.el.removeAllListeners();
30099         if(removeEl === true){
30100             this.el.update("");
30101             this.el.remove();
30102         }
30103         Roo.DialogManager.unregister(this);
30104     },
30105
30106     // private
30107     startMove : function(){
30108         if(this.proxyDrag){
30109             this.proxy.show();
30110         }
30111         if(this.constraintoviewport !== false){
30112             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30113         }
30114     },
30115
30116     // private
30117     endMove : function(){
30118         if(!this.proxyDrag){
30119             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30120         }else{
30121             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30122             this.proxy.hide();
30123         }
30124         this.refreshSize();
30125         this.adjustAssets();
30126         this.focus();
30127         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30128     },
30129
30130     /**
30131      * Brings this dialog to the front of any other visible dialogs
30132      * @return {Roo.BasicDialog} this
30133      */
30134     toFront : function(){
30135         Roo.DialogManager.bringToFront(this);
30136         return this;
30137     },
30138
30139     /**
30140      * Sends this dialog to the back (under) of any other visible dialogs
30141      * @return {Roo.BasicDialog} this
30142      */
30143     toBack : function(){
30144         Roo.DialogManager.sendToBack(this);
30145         return this;
30146     },
30147
30148     /**
30149      * Centers this dialog in the viewport
30150      * @return {Roo.BasicDialog} this
30151      */
30152     center : function(){
30153         var xy = this.el.getCenterXY(true);
30154         this.moveTo(xy[0], xy[1]);
30155         return this;
30156     },
30157
30158     /**
30159      * Moves the dialog's top-left corner to the specified point
30160      * @param {Number} x
30161      * @param {Number} y
30162      * @return {Roo.BasicDialog} this
30163      */
30164     moveTo : function(x, y){
30165         this.xy = [x,y];
30166         if(this.isVisible()){
30167             this.el.setXY(this.xy);
30168             this.adjustAssets();
30169         }
30170         return this;
30171     },
30172
30173     /**
30174      * Aligns the dialog to the specified element
30175      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30176      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30177      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30178      * @return {Roo.BasicDialog} this
30179      */
30180     alignTo : function(element, position, offsets){
30181         this.xy = this.el.getAlignToXY(element, position, offsets);
30182         if(this.isVisible()){
30183             this.el.setXY(this.xy);
30184             this.adjustAssets();
30185         }
30186         return this;
30187     },
30188
30189     /**
30190      * Anchors an element to another element and realigns it when the window is resized.
30191      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30192      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30193      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30194      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30195      * is a number, it is used as the buffer delay (defaults to 50ms).
30196      * @return {Roo.BasicDialog} this
30197      */
30198     anchorTo : function(el, alignment, offsets, monitorScroll){
30199         var action = function(){
30200             this.alignTo(el, alignment, offsets);
30201         };
30202         Roo.EventManager.onWindowResize(action, this);
30203         var tm = typeof monitorScroll;
30204         if(tm != 'undefined'){
30205             Roo.EventManager.on(window, 'scroll', action, this,
30206                 {buffer: tm == 'number' ? monitorScroll : 50});
30207         }
30208         action.call(this);
30209         return this;
30210     },
30211
30212     /**
30213      * Returns true if the dialog is visible
30214      * @return {Boolean}
30215      */
30216     isVisible : function(){
30217         return this.el.isVisible();
30218     },
30219
30220     // private
30221     animHide : function(callback){
30222         var b = Roo.get(this.animateTarget).getBox();
30223         this.proxy.show();
30224         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30225         this.el.hide();
30226         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30227                     this.hideEl.createDelegate(this, [callback]));
30228     },
30229
30230     /**
30231      * Hides the dialog.
30232      * @param {Function} callback (optional) Function to call when the dialog is hidden
30233      * @return {Roo.BasicDialog} this
30234      */
30235     hide : function(callback){
30236         if (this.fireEvent("beforehide", this) === false){
30237             return;
30238         }
30239         if(this.shadow){
30240             this.shadow.hide();
30241         }
30242         if(this.shim) {
30243           this.shim.hide();
30244         }
30245         // sometimes animateTarget seems to get set.. causing problems...
30246         // this just double checks..
30247         if(this.animateTarget && Roo.get(this.animateTarget)) {
30248            this.animHide(callback);
30249         }else{
30250             this.el.hide();
30251             this.hideEl(callback);
30252         }
30253         return this;
30254     },
30255
30256     // private
30257     hideEl : function(callback){
30258         this.proxy.hide();
30259         if(this.modal){
30260             this.mask.hide();
30261             Roo.get(document.body).removeClass("x-body-masked");
30262         }
30263         this.fireEvent("hide", this);
30264         if(typeof callback == "function"){
30265             callback();
30266         }
30267     },
30268
30269     // private
30270     hideAction : function(){
30271         this.setLeft("-10000px");
30272         this.setTop("-10000px");
30273         this.setStyle("visibility", "hidden");
30274     },
30275
30276     // private
30277     refreshSize : function(){
30278         this.size = this.el.getSize();
30279         this.xy = this.el.getXY();
30280         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30281     },
30282
30283     // private
30284     // z-index is managed by the DialogManager and may be overwritten at any time
30285     setZIndex : function(index){
30286         if(this.modal){
30287             this.mask.setStyle("z-index", index);
30288         }
30289         if(this.shim){
30290             this.shim.setStyle("z-index", ++index);
30291         }
30292         if(this.shadow){
30293             this.shadow.setZIndex(++index);
30294         }
30295         this.el.setStyle("z-index", ++index);
30296         if(this.proxy){
30297             this.proxy.setStyle("z-index", ++index);
30298         }
30299         if(this.resizer){
30300             this.resizer.proxy.setStyle("z-index", ++index);
30301         }
30302
30303         this.lastZIndex = index;
30304     },
30305
30306     /**
30307      * Returns the element for this dialog
30308      * @return {Roo.Element} The underlying dialog Element
30309      */
30310     getEl : function(){
30311         return this.el;
30312     }
30313 });
30314
30315 /**
30316  * @class Roo.DialogManager
30317  * Provides global access to BasicDialogs that have been created and
30318  * support for z-indexing (layering) multiple open dialogs.
30319  */
30320 Roo.DialogManager = function(){
30321     var list = {};
30322     var accessList = [];
30323     var front = null;
30324
30325     // private
30326     var sortDialogs = function(d1, d2){
30327         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30328     };
30329
30330     // private
30331     var orderDialogs = function(){
30332         accessList.sort(sortDialogs);
30333         var seed = Roo.DialogManager.zseed;
30334         for(var i = 0, len = accessList.length; i < len; i++){
30335             var dlg = accessList[i];
30336             if(dlg){
30337                 dlg.setZIndex(seed + (i*10));
30338             }
30339         }
30340     };
30341
30342     return {
30343         /**
30344          * The starting z-index for BasicDialogs (defaults to 9000)
30345          * @type Number The z-index value
30346          */
30347         zseed : 9000,
30348
30349         // private
30350         register : function(dlg){
30351             list[dlg.id] = dlg;
30352             accessList.push(dlg);
30353         },
30354
30355         // private
30356         unregister : function(dlg){
30357             delete list[dlg.id];
30358             var i=0;
30359             var len=0;
30360             if(!accessList.indexOf){
30361                 for(  i = 0, len = accessList.length; i < len; i++){
30362                     if(accessList[i] == dlg){
30363                         accessList.splice(i, 1);
30364                         return;
30365                     }
30366                 }
30367             }else{
30368                  i = accessList.indexOf(dlg);
30369                 if(i != -1){
30370                     accessList.splice(i, 1);
30371                 }
30372             }
30373         },
30374
30375         /**
30376          * Gets a registered dialog by id
30377          * @param {String/Object} id The id of the dialog or a dialog
30378          * @return {Roo.BasicDialog} this
30379          */
30380         get : function(id){
30381             return typeof id == "object" ? id : list[id];
30382         },
30383
30384         /**
30385          * Brings the specified dialog to the front
30386          * @param {String/Object} dlg The id of the dialog or a dialog
30387          * @return {Roo.BasicDialog} this
30388          */
30389         bringToFront : function(dlg){
30390             dlg = this.get(dlg);
30391             if(dlg != front){
30392                 front = dlg;
30393                 dlg._lastAccess = new Date().getTime();
30394                 orderDialogs();
30395             }
30396             return dlg;
30397         },
30398
30399         /**
30400          * Sends the specified dialog to the back
30401          * @param {String/Object} dlg The id of the dialog or a dialog
30402          * @return {Roo.BasicDialog} this
30403          */
30404         sendToBack : function(dlg){
30405             dlg = this.get(dlg);
30406             dlg._lastAccess = -(new Date().getTime());
30407             orderDialogs();
30408             return dlg;
30409         },
30410
30411         /**
30412          * Hides all dialogs
30413          */
30414         hideAll : function(){
30415             for(var id in list){
30416                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
30417                     list[id].hide();
30418                 }
30419             }
30420         }
30421     };
30422 }();
30423
30424 /**
30425  * @class Roo.LayoutDialog
30426  * @extends Roo.BasicDialog
30427  * Dialog which provides adjustments for working with a layout in a Dialog.
30428  * Add your necessary layout config options to the dialog's config.<br>
30429  * Example usage (including a nested layout):
30430  * <pre><code>
30431 if(!dialog){
30432     dialog = new Roo.LayoutDialog("download-dlg", {
30433         modal: true,
30434         width:600,
30435         height:450,
30436         shadow:true,
30437         minWidth:500,
30438         minHeight:350,
30439         autoTabs:true,
30440         proxyDrag:true,
30441         // layout config merges with the dialog config
30442         center:{
30443             tabPosition: "top",
30444             alwaysShowTabs: true
30445         }
30446     });
30447     dialog.addKeyListener(27, dialog.hide, dialog);
30448     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
30449     dialog.addButton("Build It!", this.getDownload, this);
30450
30451     // we can even add nested layouts
30452     var innerLayout = new Roo.BorderLayout("dl-inner", {
30453         east: {
30454             initialSize: 200,
30455             autoScroll:true,
30456             split:true
30457         },
30458         center: {
30459             autoScroll:true
30460         }
30461     });
30462     innerLayout.beginUpdate();
30463     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
30464     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
30465     innerLayout.endUpdate(true);
30466
30467     var layout = dialog.getLayout();
30468     layout.beginUpdate();
30469     layout.add("center", new Roo.ContentPanel("standard-panel",
30470                         {title: "Download the Source", fitToFrame:true}));
30471     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
30472                {title: "Build your own roo.js"}));
30473     layout.getRegion("center").showPanel(sp);
30474     layout.endUpdate();
30475 }
30476 </code></pre>
30477     * @constructor
30478     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
30479     * @param {Object} config configuration options
30480   */
30481 Roo.LayoutDialog = function(el, cfg){
30482     
30483     var config=  cfg;
30484     if (typeof(cfg) == 'undefined') {
30485         config = Roo.apply({}, el);
30486         // not sure why we use documentElement here.. - it should always be body.
30487         // IE7 borks horribly if we use documentElement.
30488         // webkit also does not like documentElement - it creates a body element...
30489         el = Roo.get( document.body || document.documentElement ).createChild();
30490         //config.autoCreate = true;
30491     }
30492     
30493     
30494     config.autoTabs = false;
30495     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
30496     this.body.setStyle({overflow:"hidden", position:"relative"});
30497     this.layout = new Roo.BorderLayout(this.body.dom, config);
30498     this.layout.monitorWindowResize = false;
30499     this.el.addClass("x-dlg-auto-layout");
30500     // fix case when center region overwrites center function
30501     this.center = Roo.BasicDialog.prototype.center;
30502     this.on("show", this.layout.layout, this.layout, true);
30503     if (config.items) {
30504         var xitems = config.items;
30505         delete config.items;
30506         Roo.each(xitems, this.addxtype, this);
30507     }
30508     
30509     
30510 };
30511 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
30512     /**
30513      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
30514      * @deprecated
30515      */
30516     endUpdate : function(){
30517         this.layout.endUpdate();
30518     },
30519
30520     /**
30521      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
30522      *  @deprecated
30523      */
30524     beginUpdate : function(){
30525         this.layout.beginUpdate();
30526     },
30527
30528     /**
30529      * Get the BorderLayout for this dialog
30530      * @return {Roo.BorderLayout}
30531      */
30532     getLayout : function(){
30533         return this.layout;
30534     },
30535
30536     showEl : function(){
30537         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
30538         if(Roo.isIE7){
30539             this.layout.layout();
30540         }
30541     },
30542
30543     // private
30544     // Use the syncHeightBeforeShow config option to control this automatically
30545     syncBodyHeight : function(){
30546         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
30547         if(this.layout){this.layout.layout();}
30548     },
30549     
30550       /**
30551      * Add an xtype element (actually adds to the layout.)
30552      * @return {Object} xdata xtype object data.
30553      */
30554     
30555     addxtype : function(c) {
30556         return this.layout.addxtype(c);
30557     }
30558 });/*
30559  * Based on:
30560  * Ext JS Library 1.1.1
30561  * Copyright(c) 2006-2007, Ext JS, LLC.
30562  *
30563  * Originally Released Under LGPL - original licence link has changed is not relivant.
30564  *
30565  * Fork - LGPL
30566  * <script type="text/javascript">
30567  */
30568  
30569 /**
30570  * @class Roo.MessageBox
30571  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
30572  * Example usage:
30573  *<pre><code>
30574 // Basic alert:
30575 Roo.Msg.alert('Status', 'Changes saved successfully.');
30576
30577 // Prompt for user data:
30578 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
30579     if (btn == 'ok'){
30580         // process text value...
30581     }
30582 });
30583
30584 // Show a dialog using config options:
30585 Roo.Msg.show({
30586    title:'Save Changes?',
30587    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
30588    buttons: Roo.Msg.YESNOCANCEL,
30589    fn: processResult,
30590    animEl: 'elId'
30591 });
30592 </code></pre>
30593  * @singleton
30594  */
30595 Roo.MessageBox = function(){
30596     var dlg, opt, mask, waitTimer;
30597     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30598     var buttons, activeTextEl, bwidth;
30599
30600     // private
30601     var handleButton = function(button){
30602         dlg.hide();
30603         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30604     };
30605
30606     // private
30607     var handleHide = function(){
30608         if(opt && opt.cls){
30609             dlg.el.removeClass(opt.cls);
30610         }
30611         if(waitTimer){
30612             Roo.TaskMgr.stop(waitTimer);
30613             waitTimer = null;
30614         }
30615     };
30616
30617     // private
30618     var updateButtons = function(b){
30619         var width = 0;
30620         if(!b){
30621             buttons["ok"].hide();
30622             buttons["cancel"].hide();
30623             buttons["yes"].hide();
30624             buttons["no"].hide();
30625             dlg.footer.dom.style.display = 'none';
30626             return width;
30627         }
30628         dlg.footer.dom.style.display = '';
30629         for(var k in buttons){
30630             if(typeof buttons[k] != "function"){
30631                 if(b[k]){
30632                     buttons[k].show();
30633                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30634                     width += buttons[k].el.getWidth()+15;
30635                 }else{
30636                     buttons[k].hide();
30637                 }
30638             }
30639         }
30640         return width;
30641     };
30642
30643     // private
30644     var handleEsc = function(d, k, e){
30645         if(opt && opt.closable !== false){
30646             dlg.hide();
30647         }
30648         if(e){
30649             e.stopEvent();
30650         }
30651     };
30652
30653     return {
30654         /**
30655          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30656          * @return {Roo.BasicDialog} The BasicDialog element
30657          */
30658         getDialog : function(){
30659            if(!dlg){
30660                 dlg = new Roo.BasicDialog("x-msg-box", {
30661                     autoCreate : true,
30662                     shadow: true,
30663                     draggable: true,
30664                     resizable:false,
30665                     constraintoviewport:false,
30666                     fixedcenter:true,
30667                     collapsible : false,
30668                     shim:true,
30669                     modal: true,
30670                     width:400, height:100,
30671                     buttonAlign:"center",
30672                     closeClick : function(){
30673                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30674                             handleButton("no");
30675                         }else{
30676                             handleButton("cancel");
30677                         }
30678                     }
30679                 });
30680                 dlg.on("hide", handleHide);
30681                 mask = dlg.mask;
30682                 dlg.addKeyListener(27, handleEsc);
30683                 buttons = {};
30684                 var bt = this.buttonText;
30685                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30686                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30687                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30688                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30689                 bodyEl = dlg.body.createChild({
30690
30691                     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>'
30692                 });
30693                 msgEl = bodyEl.dom.firstChild;
30694                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30695                 textboxEl.enableDisplayMode();
30696                 textboxEl.addKeyListener([10,13], function(){
30697                     if(dlg.isVisible() && opt && opt.buttons){
30698                         if(opt.buttons.ok){
30699                             handleButton("ok");
30700                         }else if(opt.buttons.yes){
30701                             handleButton("yes");
30702                         }
30703                     }
30704                 });
30705                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30706                 textareaEl.enableDisplayMode();
30707                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30708                 progressEl.enableDisplayMode();
30709                 var pf = progressEl.dom.firstChild;
30710                 if (pf) {
30711                     pp = Roo.get(pf.firstChild);
30712                     pp.setHeight(pf.offsetHeight);
30713                 }
30714                 
30715             }
30716             return dlg;
30717         },
30718
30719         /**
30720          * Updates the message box body text
30721          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30722          * the XHTML-compliant non-breaking space character '&amp;#160;')
30723          * @return {Roo.MessageBox} This message box
30724          */
30725         updateText : function(text){
30726             if(!dlg.isVisible() && !opt.width){
30727                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30728             }
30729             msgEl.innerHTML = text || '&#160;';
30730       
30731             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30732             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30733             var w = Math.max(
30734                     Math.min(opt.width || cw , this.maxWidth), 
30735                     Math.max(opt.minWidth || this.minWidth, bwidth)
30736             );
30737             if(opt.prompt){
30738                 activeTextEl.setWidth(w);
30739             }
30740             if(dlg.isVisible()){
30741                 dlg.fixedcenter = false;
30742             }
30743             // to big, make it scroll. = But as usual stupid IE does not support
30744             // !important..
30745             
30746             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30747                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30748                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30749             } else {
30750                 bodyEl.dom.style.height = '';
30751                 bodyEl.dom.style.overflowY = '';
30752             }
30753             if (cw > w) {
30754                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30755             } else {
30756                 bodyEl.dom.style.overflowX = '';
30757             }
30758             
30759             dlg.setContentSize(w, bodyEl.getHeight());
30760             if(dlg.isVisible()){
30761                 dlg.fixedcenter = true;
30762             }
30763             return this;
30764         },
30765
30766         /**
30767          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30768          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30769          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30770          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30771          * @return {Roo.MessageBox} This message box
30772          */
30773         updateProgress : function(value, text){
30774             if(text){
30775                 this.updateText(text);
30776             }
30777             if (pp) { // weird bug on my firefox - for some reason this is not defined
30778                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30779             }
30780             return this;
30781         },        
30782
30783         /**
30784          * Returns true if the message box is currently displayed
30785          * @return {Boolean} True if the message box is visible, else false
30786          */
30787         isVisible : function(){
30788             return dlg && dlg.isVisible();  
30789         },
30790
30791         /**
30792          * Hides the message box if it is displayed
30793          */
30794         hide : function(){
30795             if(this.isVisible()){
30796                 dlg.hide();
30797             }  
30798         },
30799
30800         /**
30801          * Displays a new message box, or reinitializes an existing message box, based on the config options
30802          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30803          * The following config object properties are supported:
30804          * <pre>
30805 Property    Type             Description
30806 ----------  ---------------  ------------------------------------------------------------------------------------
30807 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30808                                    closes (defaults to undefined)
30809 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30810                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30811 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30812                                    progress and wait dialogs will ignore this property and always hide the
30813                                    close button as they can only be closed programmatically.
30814 cls               String           A custom CSS class to apply to the message box element
30815 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30816                                    displayed (defaults to 75)
30817 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30818                                    function will be btn (the name of the button that was clicked, if applicable,
30819                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30820                                    Progress and wait dialogs will ignore this option since they do not respond to
30821                                    user actions and can only be closed programmatically, so any required function
30822                                    should be called by the same code after it closes the dialog.
30823 icon              String           A CSS class that provides a background image to be used as an icon for
30824                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30825 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30826 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30827 modal             Boolean          False to allow user interaction with the page while the message box is
30828                                    displayed (defaults to true)
30829 msg               String           A string that will replace the existing message box body text (defaults
30830                                    to the XHTML-compliant non-breaking space character '&#160;')
30831 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30832 progress          Boolean          True to display a progress bar (defaults to false)
30833 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30834 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30835 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30836 title             String           The title text
30837 value             String           The string value to set into the active textbox element if displayed
30838 wait              Boolean          True to display a progress bar (defaults to false)
30839 width             Number           The width of the dialog in pixels
30840 </pre>
30841          *
30842          * Example usage:
30843          * <pre><code>
30844 Roo.Msg.show({
30845    title: 'Address',
30846    msg: 'Please enter your address:',
30847    width: 300,
30848    buttons: Roo.MessageBox.OKCANCEL,
30849    multiline: true,
30850    fn: saveAddress,
30851    animEl: 'addAddressBtn'
30852 });
30853 </code></pre>
30854          * @param {Object} config Configuration options
30855          * @return {Roo.MessageBox} This message box
30856          */
30857         show : function(options)
30858         {
30859             
30860             // this causes nightmares if you show one dialog after another
30861             // especially on callbacks..
30862              
30863             if(this.isVisible()){
30864                 
30865                 this.hide();
30866                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30867                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30868                 Roo.log("New Dialog Message:" +  options.msg )
30869                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30870                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30871                 
30872             }
30873             var d = this.getDialog();
30874             opt = options;
30875             d.setTitle(opt.title || "&#160;");
30876             d.close.setDisplayed(opt.closable !== false);
30877             activeTextEl = textboxEl;
30878             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30879             if(opt.prompt){
30880                 if(opt.multiline){
30881                     textboxEl.hide();
30882                     textareaEl.show();
30883                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30884                         opt.multiline : this.defaultTextHeight);
30885                     activeTextEl = textareaEl;
30886                 }else{
30887                     textboxEl.show();
30888                     textareaEl.hide();
30889                 }
30890             }else{
30891                 textboxEl.hide();
30892                 textareaEl.hide();
30893             }
30894             progressEl.setDisplayed(opt.progress === true);
30895             this.updateProgress(0);
30896             activeTextEl.dom.value = opt.value || "";
30897             if(opt.prompt){
30898                 dlg.setDefaultButton(activeTextEl);
30899             }else{
30900                 var bs = opt.buttons;
30901                 var db = null;
30902                 if(bs && bs.ok){
30903                     db = buttons["ok"];
30904                 }else if(bs && bs.yes){
30905                     db = buttons["yes"];
30906                 }
30907                 dlg.setDefaultButton(db);
30908             }
30909             bwidth = updateButtons(opt.buttons);
30910             this.updateText(opt.msg);
30911             if(opt.cls){
30912                 d.el.addClass(opt.cls);
30913             }
30914             d.proxyDrag = opt.proxyDrag === true;
30915             d.modal = opt.modal !== false;
30916             d.mask = opt.modal !== false ? mask : false;
30917             if(!d.isVisible()){
30918                 // force it to the end of the z-index stack so it gets a cursor in FF
30919                 document.body.appendChild(dlg.el.dom);
30920                 d.animateTarget = null;
30921                 d.show(options.animEl);
30922             }
30923             return this;
30924         },
30925
30926         /**
30927          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30928          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30929          * and closing the message box when the process is complete.
30930          * @param {String} title The title bar text
30931          * @param {String} msg The message box body text
30932          * @return {Roo.MessageBox} This message box
30933          */
30934         progress : function(title, msg){
30935             this.show({
30936                 title : title,
30937                 msg : msg,
30938                 buttons: false,
30939                 progress:true,
30940                 closable:false,
30941                 minWidth: this.minProgressWidth,
30942                 modal : true
30943             });
30944             return this;
30945         },
30946
30947         /**
30948          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30949          * If a callback function is passed it will be called after the user clicks the button, and the
30950          * id of the button that was clicked will be passed as the only parameter to the callback
30951          * (could also be the top-right close button).
30952          * @param {String} title The title bar text
30953          * @param {String} msg The message box body text
30954          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30955          * @param {Object} scope (optional) The scope of the callback function
30956          * @return {Roo.MessageBox} This message box
30957          */
30958         alert : function(title, msg, fn, scope){
30959             this.show({
30960                 title : title,
30961                 msg : msg,
30962                 buttons: this.OK,
30963                 fn: fn,
30964                 scope : scope,
30965                 modal : true
30966             });
30967             return this;
30968         },
30969
30970         /**
30971          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30972          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30973          * You are responsible for closing the message box when the process is complete.
30974          * @param {String} msg The message box body text
30975          * @param {String} title (optional) The title bar text
30976          * @return {Roo.MessageBox} This message box
30977          */
30978         wait : function(msg, title){
30979             this.show({
30980                 title : title,
30981                 msg : msg,
30982                 buttons: false,
30983                 closable:false,
30984                 progress:true,
30985                 modal:true,
30986                 width:300,
30987                 wait:true
30988             });
30989             waitTimer = Roo.TaskMgr.start({
30990                 run: function(i){
30991                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30992                 },
30993                 interval: 1000
30994             });
30995             return this;
30996         },
30997
30998         /**
30999          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31000          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31001          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31002          * @param {String} title The title bar text
31003          * @param {String} msg The message box body text
31004          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31005          * @param {Object} scope (optional) The scope of the callback function
31006          * @return {Roo.MessageBox} This message box
31007          */
31008         confirm : function(title, msg, fn, scope){
31009             this.show({
31010                 title : title,
31011                 msg : msg,
31012                 buttons: this.YESNO,
31013                 fn: fn,
31014                 scope : scope,
31015                 modal : true
31016             });
31017             return this;
31018         },
31019
31020         /**
31021          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31022          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31023          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31024          * (could also be the top-right close button) and the text that was entered will be passed as the two
31025          * parameters to the callback.
31026          * @param {String} title The title bar text
31027          * @param {String} msg The message box body text
31028          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31029          * @param {Object} scope (optional) The scope of the callback function
31030          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31031          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31032          * @return {Roo.MessageBox} This message box
31033          */
31034         prompt : function(title, msg, fn, scope, multiline){
31035             this.show({
31036                 title : title,
31037                 msg : msg,
31038                 buttons: this.OKCANCEL,
31039                 fn: fn,
31040                 minWidth:250,
31041                 scope : scope,
31042                 prompt:true,
31043                 multiline: multiline,
31044                 modal : true
31045             });
31046             return this;
31047         },
31048
31049         /**
31050          * Button config that displays a single OK button
31051          * @type Object
31052          */
31053         OK : {ok:true},
31054         /**
31055          * Button config that displays Yes and No buttons
31056          * @type Object
31057          */
31058         YESNO : {yes:true, no:true},
31059         /**
31060          * Button config that displays OK and Cancel buttons
31061          * @type Object
31062          */
31063         OKCANCEL : {ok:true, cancel:true},
31064         /**
31065          * Button config that displays Yes, No and Cancel buttons
31066          * @type Object
31067          */
31068         YESNOCANCEL : {yes:true, no:true, cancel:true},
31069
31070         /**
31071          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31072          * @type Number
31073          */
31074         defaultTextHeight : 75,
31075         /**
31076          * The maximum width in pixels of the message box (defaults to 600)
31077          * @type Number
31078          */
31079         maxWidth : 600,
31080         /**
31081          * The minimum width in pixels of the message box (defaults to 100)
31082          * @type Number
31083          */
31084         minWidth : 100,
31085         /**
31086          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31087          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31088          * @type Number
31089          */
31090         minProgressWidth : 250,
31091         /**
31092          * An object containing the default button text strings that can be overriden for localized language support.
31093          * Supported properties are: ok, cancel, yes and no.
31094          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31095          * @type Object
31096          */
31097         buttonText : {
31098             ok : "OK",
31099             cancel : "Cancel",
31100             yes : "Yes",
31101             no : "No"
31102         }
31103     };
31104 }();
31105
31106 /**
31107  * Shorthand for {@link Roo.MessageBox}
31108  */
31109 Roo.Msg = Roo.MessageBox;/*
31110  * Based on:
31111  * Ext JS Library 1.1.1
31112  * Copyright(c) 2006-2007, Ext JS, LLC.
31113  *
31114  * Originally Released Under LGPL - original licence link has changed is not relivant.
31115  *
31116  * Fork - LGPL
31117  * <script type="text/javascript">
31118  */
31119 /**
31120  * @class Roo.QuickTips
31121  * Provides attractive and customizable tooltips for any element.
31122  * @singleton
31123  */
31124 Roo.QuickTips = function(){
31125     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31126     var ce, bd, xy, dd;
31127     var visible = false, disabled = true, inited = false;
31128     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31129     
31130     var onOver = function(e){
31131         if(disabled){
31132             return;
31133         }
31134         var t = e.getTarget();
31135         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31136             return;
31137         }
31138         if(ce && t == ce.el){
31139             clearTimeout(hideProc);
31140             return;
31141         }
31142         if(t && tagEls[t.id]){
31143             tagEls[t.id].el = t;
31144             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31145             return;
31146         }
31147         var ttp, et = Roo.fly(t);
31148         var ns = cfg.namespace;
31149         if(tm.interceptTitles && t.title){
31150             ttp = t.title;
31151             t.qtip = ttp;
31152             t.removeAttribute("title");
31153             e.preventDefault();
31154         }else{
31155             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31156         }
31157         if(ttp){
31158             showProc = show.defer(tm.showDelay, tm, [{
31159                 el: t, 
31160                 text: ttp, 
31161                 width: et.getAttributeNS(ns, cfg.width),
31162                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31163                 title: et.getAttributeNS(ns, cfg.title),
31164                     cls: et.getAttributeNS(ns, cfg.cls)
31165             }]);
31166         }
31167     };
31168     
31169     var onOut = function(e){
31170         clearTimeout(showProc);
31171         var t = e.getTarget();
31172         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31173             hideProc = setTimeout(hide, tm.hideDelay);
31174         }
31175     };
31176     
31177     var onMove = function(e){
31178         if(disabled){
31179             return;
31180         }
31181         xy = e.getXY();
31182         xy[1] += 18;
31183         if(tm.trackMouse && ce){
31184             el.setXY(xy);
31185         }
31186     };
31187     
31188     var onDown = function(e){
31189         clearTimeout(showProc);
31190         clearTimeout(hideProc);
31191         if(!e.within(el)){
31192             if(tm.hideOnClick){
31193                 hide();
31194                 tm.disable();
31195                 tm.enable.defer(100, tm);
31196             }
31197         }
31198     };
31199     
31200     var getPad = function(){
31201         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31202     };
31203
31204     var show = function(o){
31205         if(disabled){
31206             return;
31207         }
31208         clearTimeout(dismissProc);
31209         ce = o;
31210         if(removeCls){ // in case manually hidden
31211             el.removeClass(removeCls);
31212             removeCls = null;
31213         }
31214         if(ce.cls){
31215             el.addClass(ce.cls);
31216             removeCls = ce.cls;
31217         }
31218         if(ce.title){
31219             tipTitle.update(ce.title);
31220             tipTitle.show();
31221         }else{
31222             tipTitle.update('');
31223             tipTitle.hide();
31224         }
31225         el.dom.style.width  = tm.maxWidth+'px';
31226         //tipBody.dom.style.width = '';
31227         tipBodyText.update(o.text);
31228         var p = getPad(), w = ce.width;
31229         if(!w){
31230             var td = tipBodyText.dom;
31231             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31232             if(aw > tm.maxWidth){
31233                 w = tm.maxWidth;
31234             }else if(aw < tm.minWidth){
31235                 w = tm.minWidth;
31236             }else{
31237                 w = aw;
31238             }
31239         }
31240         //tipBody.setWidth(w);
31241         el.setWidth(parseInt(w, 10) + p);
31242         if(ce.autoHide === false){
31243             close.setDisplayed(true);
31244             if(dd){
31245                 dd.unlock();
31246             }
31247         }else{
31248             close.setDisplayed(false);
31249             if(dd){
31250                 dd.lock();
31251             }
31252         }
31253         if(xy){
31254             el.avoidY = xy[1]-18;
31255             el.setXY(xy);
31256         }
31257         if(tm.animate){
31258             el.setOpacity(.1);
31259             el.setStyle("visibility", "visible");
31260             el.fadeIn({callback: afterShow});
31261         }else{
31262             afterShow();
31263         }
31264     };
31265     
31266     var afterShow = function(){
31267         if(ce){
31268             el.show();
31269             esc.enable();
31270             if(tm.autoDismiss && ce.autoHide !== false){
31271                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31272             }
31273         }
31274     };
31275     
31276     var hide = function(noanim){
31277         clearTimeout(dismissProc);
31278         clearTimeout(hideProc);
31279         ce = null;
31280         if(el.isVisible()){
31281             esc.disable();
31282             if(noanim !== true && tm.animate){
31283                 el.fadeOut({callback: afterHide});
31284             }else{
31285                 afterHide();
31286             } 
31287         }
31288     };
31289     
31290     var afterHide = function(){
31291         el.hide();
31292         if(removeCls){
31293             el.removeClass(removeCls);
31294             removeCls = null;
31295         }
31296     };
31297     
31298     return {
31299         /**
31300         * @cfg {Number} minWidth
31301         * The minimum width of the quick tip (defaults to 40)
31302         */
31303        minWidth : 40,
31304         /**
31305         * @cfg {Number} maxWidth
31306         * The maximum width of the quick tip (defaults to 300)
31307         */
31308        maxWidth : 300,
31309         /**
31310         * @cfg {Boolean} interceptTitles
31311         * True to automatically use the element's DOM title value if available (defaults to false)
31312         */
31313        interceptTitles : false,
31314         /**
31315         * @cfg {Boolean} trackMouse
31316         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31317         */
31318        trackMouse : false,
31319         /**
31320         * @cfg {Boolean} hideOnClick
31321         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31322         */
31323        hideOnClick : true,
31324         /**
31325         * @cfg {Number} showDelay
31326         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31327         */
31328        showDelay : 500,
31329         /**
31330         * @cfg {Number} hideDelay
31331         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31332         */
31333        hideDelay : 200,
31334         /**
31335         * @cfg {Boolean} autoHide
31336         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31337         * Used in conjunction with hideDelay.
31338         */
31339        autoHide : true,
31340         /**
31341         * @cfg {Boolean}
31342         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31343         * (defaults to true).  Used in conjunction with autoDismissDelay.
31344         */
31345        autoDismiss : true,
31346         /**
31347         * @cfg {Number}
31348         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31349         */
31350        autoDismissDelay : 5000,
31351        /**
31352         * @cfg {Boolean} animate
31353         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31354         */
31355        animate : false,
31356
31357        /**
31358         * @cfg {String} title
31359         * Title text to display (defaults to '').  This can be any valid HTML markup.
31360         */
31361         title: '',
31362        /**
31363         * @cfg {String} text
31364         * Body text to display (defaults to '').  This can be any valid HTML markup.
31365         */
31366         text : '',
31367        /**
31368         * @cfg {String} cls
31369         * A CSS class to apply to the base quick tip element (defaults to '').
31370         */
31371         cls : '',
31372        /**
31373         * @cfg {Number} width
31374         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
31375         * minWidth or maxWidth.
31376         */
31377         width : null,
31378
31379     /**
31380      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
31381      * or display QuickTips in a page.
31382      */
31383        init : function(){
31384           tm = Roo.QuickTips;
31385           cfg = tm.tagConfig;
31386           if(!inited){
31387               if(!Roo.isReady){ // allow calling of init() before onReady
31388                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
31389                   return;
31390               }
31391               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
31392               el.fxDefaults = {stopFx: true};
31393               // maximum custom styling
31394               //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>');
31395               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>');              
31396               tipTitle = el.child('h3');
31397               tipTitle.enableDisplayMode("block");
31398               tipBody = el.child('div.x-tip-bd');
31399               tipBodyText = el.child('div.x-tip-bd-inner');
31400               //bdLeft = el.child('div.x-tip-bd-left');
31401               //bdRight = el.child('div.x-tip-bd-right');
31402               close = el.child('div.x-tip-close');
31403               close.enableDisplayMode("block");
31404               close.on("click", hide);
31405               var d = Roo.get(document);
31406               d.on("mousedown", onDown);
31407               d.on("mouseover", onOver);
31408               d.on("mouseout", onOut);
31409               d.on("mousemove", onMove);
31410               esc = d.addKeyListener(27, hide);
31411               esc.disable();
31412               if(Roo.dd.DD){
31413                   dd = el.initDD("default", null, {
31414                       onDrag : function(){
31415                           el.sync();  
31416                       }
31417                   });
31418                   dd.setHandleElId(tipTitle.id);
31419                   dd.lock();
31420               }
31421               inited = true;
31422           }
31423           this.enable(); 
31424        },
31425
31426     /**
31427      * Configures a new quick tip instance and assigns it to a target element.  The following config options
31428      * are supported:
31429      * <pre>
31430 Property    Type                   Description
31431 ----------  ---------------------  ------------------------------------------------------------------------
31432 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
31433      * </ul>
31434      * @param {Object} config The config object
31435      */
31436        register : function(config){
31437            var cs = config instanceof Array ? config : arguments;
31438            for(var i = 0, len = cs.length; i < len; i++) {
31439                var c = cs[i];
31440                var target = c.target;
31441                if(target){
31442                    if(target instanceof Array){
31443                        for(var j = 0, jlen = target.length; j < jlen; j++){
31444                            tagEls[target[j]] = c;
31445                        }
31446                    }else{
31447                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
31448                    }
31449                }
31450            }
31451        },
31452
31453     /**
31454      * Removes this quick tip from its element and destroys it.
31455      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
31456      */
31457        unregister : function(el){
31458            delete tagEls[Roo.id(el)];
31459        },
31460
31461     /**
31462      * Enable this quick tip.
31463      */
31464        enable : function(){
31465            if(inited && disabled){
31466                locks.pop();
31467                if(locks.length < 1){
31468                    disabled = false;
31469                }
31470            }
31471        },
31472
31473     /**
31474      * Disable this quick tip.
31475      */
31476        disable : function(){
31477           disabled = true;
31478           clearTimeout(showProc);
31479           clearTimeout(hideProc);
31480           clearTimeout(dismissProc);
31481           if(ce){
31482               hide(true);
31483           }
31484           locks.push(1);
31485        },
31486
31487     /**
31488      * Returns true if the quick tip is enabled, else false.
31489      */
31490        isEnabled : function(){
31491             return !disabled;
31492        },
31493
31494         // private
31495        tagConfig : {
31496            namespace : "ext",
31497            attribute : "qtip",
31498            width : "width",
31499            target : "target",
31500            title : "qtitle",
31501            hide : "hide",
31502            cls : "qclass"
31503        }
31504    };
31505 }();
31506
31507 // backwards compat
31508 Roo.QuickTips.tips = Roo.QuickTips.register;/*
31509  * Based on:
31510  * Ext JS Library 1.1.1
31511  * Copyright(c) 2006-2007, Ext JS, LLC.
31512  *
31513  * Originally Released Under LGPL - original licence link has changed is not relivant.
31514  *
31515  * Fork - LGPL
31516  * <script type="text/javascript">
31517  */
31518  
31519
31520 /**
31521  * @class Roo.tree.TreePanel
31522  * @extends Roo.data.Tree
31523
31524  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
31525  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
31526  * @cfg {Boolean} enableDD true to enable drag and drop
31527  * @cfg {Boolean} enableDrag true to enable just drag
31528  * @cfg {Boolean} enableDrop true to enable just drop
31529  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
31530  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
31531  * @cfg {String} ddGroup The DD group this TreePanel belongs to
31532  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
31533  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
31534  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
31535  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
31536  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
31537  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
31538  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
31539  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
31540  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
31541  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
31542  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
31543  * @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>
31544  * @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>
31545  * 
31546  * @constructor
31547  * @param {String/HTMLElement/Element} el The container element
31548  * @param {Object} config
31549  */
31550 Roo.tree.TreePanel = function(el, config){
31551     var root = false;
31552     var loader = false;
31553     if (config.root) {
31554         root = config.root;
31555         delete config.root;
31556     }
31557     if (config.loader) {
31558         loader = config.loader;
31559         delete config.loader;
31560     }
31561     
31562     Roo.apply(this, config);
31563     Roo.tree.TreePanel.superclass.constructor.call(this);
31564     this.el = Roo.get(el);
31565     this.el.addClass('x-tree');
31566     //console.log(root);
31567     if (root) {
31568         this.setRootNode( Roo.factory(root, Roo.tree));
31569     }
31570     if (loader) {
31571         this.loader = Roo.factory(loader, Roo.tree);
31572     }
31573    /**
31574     * Read-only. The id of the container element becomes this TreePanel's id.
31575     */
31576     this.id = this.el.id;
31577     this.addEvents({
31578         /**
31579         * @event beforeload
31580         * Fires before a node is loaded, return false to cancel
31581         * @param {Node} node The node being loaded
31582         */
31583         "beforeload" : true,
31584         /**
31585         * @event load
31586         * Fires when a node is loaded
31587         * @param {Node} node The node that was loaded
31588         */
31589         "load" : true,
31590         /**
31591         * @event textchange
31592         * Fires when the text for a node is changed
31593         * @param {Node} node The node
31594         * @param {String} text The new text
31595         * @param {String} oldText The old text
31596         */
31597         "textchange" : true,
31598         /**
31599         * @event beforeexpand
31600         * Fires before a node is expanded, return false to cancel.
31601         * @param {Node} node The node
31602         * @param {Boolean} deep
31603         * @param {Boolean} anim
31604         */
31605         "beforeexpand" : true,
31606         /**
31607         * @event beforecollapse
31608         * Fires before a node is collapsed, return false to cancel.
31609         * @param {Node} node The node
31610         * @param {Boolean} deep
31611         * @param {Boolean} anim
31612         */
31613         "beforecollapse" : true,
31614         /**
31615         * @event expand
31616         * Fires when a node is expanded
31617         * @param {Node} node The node
31618         */
31619         "expand" : true,
31620         /**
31621         * @event disabledchange
31622         * Fires when the disabled status of a node changes
31623         * @param {Node} node The node
31624         * @param {Boolean} disabled
31625         */
31626         "disabledchange" : true,
31627         /**
31628         * @event collapse
31629         * Fires when a node is collapsed
31630         * @param {Node} node The node
31631         */
31632         "collapse" : true,
31633         /**
31634         * @event beforeclick
31635         * Fires before click processing on a node. Return false to cancel the default action.
31636         * @param {Node} node The node
31637         * @param {Roo.EventObject} e The event object
31638         */
31639         "beforeclick":true,
31640         /**
31641         * @event checkchange
31642         * Fires when a node with a checkbox's checked property changes
31643         * @param {Node} this This node
31644         * @param {Boolean} checked
31645         */
31646         "checkchange":true,
31647         /**
31648         * @event click
31649         * Fires when a node is clicked
31650         * @param {Node} node The node
31651         * @param {Roo.EventObject} e The event object
31652         */
31653         "click":true,
31654         /**
31655         * @event dblclick
31656         * Fires when a node is double clicked
31657         * @param {Node} node The node
31658         * @param {Roo.EventObject} e The event object
31659         */
31660         "dblclick":true,
31661         /**
31662         * @event contextmenu
31663         * Fires when a node is right clicked
31664         * @param {Node} node The node
31665         * @param {Roo.EventObject} e The event object
31666         */
31667         "contextmenu":true,
31668         /**
31669         * @event beforechildrenrendered
31670         * Fires right before the child nodes for a node are rendered
31671         * @param {Node} node The node
31672         */
31673         "beforechildrenrendered":true,
31674         /**
31675         * @event startdrag
31676         * Fires when a node starts being dragged
31677         * @param {Roo.tree.TreePanel} this
31678         * @param {Roo.tree.TreeNode} node
31679         * @param {event} e The raw browser event
31680         */ 
31681        "startdrag" : true,
31682        /**
31683         * @event enddrag
31684         * Fires when a drag operation is complete
31685         * @param {Roo.tree.TreePanel} this
31686         * @param {Roo.tree.TreeNode} node
31687         * @param {event} e The raw browser event
31688         */
31689        "enddrag" : true,
31690        /**
31691         * @event dragdrop
31692         * Fires when a dragged node is dropped on a valid DD target
31693         * @param {Roo.tree.TreePanel} this
31694         * @param {Roo.tree.TreeNode} node
31695         * @param {DD} dd The dd it was dropped on
31696         * @param {event} e The raw browser event
31697         */
31698        "dragdrop" : true,
31699        /**
31700         * @event beforenodedrop
31701         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31702         * passed to handlers has the following properties:<br />
31703         * <ul style="padding:5px;padding-left:16px;">
31704         * <li>tree - The TreePanel</li>
31705         * <li>target - The node being targeted for the drop</li>
31706         * <li>data - The drag data from the drag source</li>
31707         * <li>point - The point of the drop - append, above or below</li>
31708         * <li>source - The drag source</li>
31709         * <li>rawEvent - Raw mouse event</li>
31710         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31711         * to be inserted by setting them on this object.</li>
31712         * <li>cancel - Set this to true to cancel the drop.</li>
31713         * </ul>
31714         * @param {Object} dropEvent
31715         */
31716        "beforenodedrop" : true,
31717        /**
31718         * @event nodedrop
31719         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31720         * passed to handlers has the following properties:<br />
31721         * <ul style="padding:5px;padding-left:16px;">
31722         * <li>tree - The TreePanel</li>
31723         * <li>target - The node being targeted for the drop</li>
31724         * <li>data - The drag data from the drag source</li>
31725         * <li>point - The point of the drop - append, above or below</li>
31726         * <li>source - The drag source</li>
31727         * <li>rawEvent - Raw mouse event</li>
31728         * <li>dropNode - Dropped node(s).</li>
31729         * </ul>
31730         * @param {Object} dropEvent
31731         */
31732        "nodedrop" : true,
31733         /**
31734         * @event nodedragover
31735         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31736         * passed to handlers has the following properties:<br />
31737         * <ul style="padding:5px;padding-left:16px;">
31738         * <li>tree - The TreePanel</li>
31739         * <li>target - The node being targeted for the drop</li>
31740         * <li>data - The drag data from the drag source</li>
31741         * <li>point - The point of the drop - append, above or below</li>
31742         * <li>source - The drag source</li>
31743         * <li>rawEvent - Raw mouse event</li>
31744         * <li>dropNode - Drop node(s) provided by the source.</li>
31745         * <li>cancel - Set this to true to signal drop not allowed.</li>
31746         * </ul>
31747         * @param {Object} dragOverEvent
31748         */
31749        "nodedragover" : true
31750         
31751     });
31752     if(this.singleExpand){
31753        this.on("beforeexpand", this.restrictExpand, this);
31754     }
31755     if (this.editor) {
31756         this.editor.tree = this;
31757         this.editor = Roo.factory(this.editor, Roo.tree);
31758     }
31759     
31760     if (this.selModel) {
31761         this.selModel = Roo.factory(this.selModel, Roo.tree);
31762     }
31763    
31764 };
31765 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31766     rootVisible : true,
31767     animate: Roo.enableFx,
31768     lines : true,
31769     enableDD : false,
31770     hlDrop : Roo.enableFx,
31771   
31772     renderer: false,
31773     
31774     rendererTip: false,
31775     // private
31776     restrictExpand : function(node){
31777         var p = node.parentNode;
31778         if(p){
31779             if(p.expandedChild && p.expandedChild.parentNode == p){
31780                 p.expandedChild.collapse();
31781             }
31782             p.expandedChild = node;
31783         }
31784     },
31785
31786     // private override
31787     setRootNode : function(node){
31788         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31789         if(!this.rootVisible){
31790             node.ui = new Roo.tree.RootTreeNodeUI(node);
31791         }
31792         return node;
31793     },
31794
31795     /**
31796      * Returns the container element for this TreePanel
31797      */
31798     getEl : function(){
31799         return this.el;
31800     },
31801
31802     /**
31803      * Returns the default TreeLoader for this TreePanel
31804      */
31805     getLoader : function(){
31806         return this.loader;
31807     },
31808
31809     /**
31810      * Expand all nodes
31811      */
31812     expandAll : function(){
31813         this.root.expand(true);
31814     },
31815
31816     /**
31817      * Collapse all nodes
31818      */
31819     collapseAll : function(){
31820         this.root.collapse(true);
31821     },
31822
31823     /**
31824      * Returns the selection model used by this TreePanel
31825      */
31826     getSelectionModel : function(){
31827         if(!this.selModel){
31828             this.selModel = new Roo.tree.DefaultSelectionModel();
31829         }
31830         return this.selModel;
31831     },
31832
31833     /**
31834      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31835      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31836      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31837      * @return {Array}
31838      */
31839     getChecked : function(a, startNode){
31840         startNode = startNode || this.root;
31841         var r = [];
31842         var f = function(){
31843             if(this.attributes.checked){
31844                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31845             }
31846         }
31847         startNode.cascade(f);
31848         return r;
31849     },
31850
31851     /**
31852      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31853      * @param {String} path
31854      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31855      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31856      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31857      */
31858     expandPath : function(path, attr, callback){
31859         attr = attr || "id";
31860         var keys = path.split(this.pathSeparator);
31861         var curNode = this.root;
31862         if(curNode.attributes[attr] != keys[1]){ // invalid root
31863             if(callback){
31864                 callback(false, null);
31865             }
31866             return;
31867         }
31868         var index = 1;
31869         var f = function(){
31870             if(++index == keys.length){
31871                 if(callback){
31872                     callback(true, curNode);
31873                 }
31874                 return;
31875             }
31876             var c = curNode.findChild(attr, keys[index]);
31877             if(!c){
31878                 if(callback){
31879                     callback(false, curNode);
31880                 }
31881                 return;
31882             }
31883             curNode = c;
31884             c.expand(false, false, f);
31885         };
31886         curNode.expand(false, false, f);
31887     },
31888
31889     /**
31890      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31891      * @param {String} path
31892      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31893      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31894      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31895      */
31896     selectPath : function(path, attr, callback){
31897         attr = attr || "id";
31898         var keys = path.split(this.pathSeparator);
31899         var v = keys.pop();
31900         if(keys.length > 0){
31901             var f = function(success, node){
31902                 if(success && node){
31903                     var n = node.findChild(attr, v);
31904                     if(n){
31905                         n.select();
31906                         if(callback){
31907                             callback(true, n);
31908                         }
31909                     }else if(callback){
31910                         callback(false, n);
31911                     }
31912                 }else{
31913                     if(callback){
31914                         callback(false, n);
31915                     }
31916                 }
31917             };
31918             this.expandPath(keys.join(this.pathSeparator), attr, f);
31919         }else{
31920             this.root.select();
31921             if(callback){
31922                 callback(true, this.root);
31923             }
31924         }
31925     },
31926
31927     getTreeEl : function(){
31928         return this.el;
31929     },
31930
31931     /**
31932      * Trigger rendering of this TreePanel
31933      */
31934     render : function(){
31935         if (this.innerCt) {
31936             return this; // stop it rendering more than once!!
31937         }
31938         
31939         this.innerCt = this.el.createChild({tag:"ul",
31940                cls:"x-tree-root-ct " +
31941                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31942
31943         if(this.containerScroll){
31944             Roo.dd.ScrollManager.register(this.el);
31945         }
31946         if((this.enableDD || this.enableDrop) && !this.dropZone){
31947            /**
31948             * The dropZone used by this tree if drop is enabled
31949             * @type Roo.tree.TreeDropZone
31950             */
31951              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31952                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31953            });
31954         }
31955         if((this.enableDD || this.enableDrag) && !this.dragZone){
31956            /**
31957             * The dragZone used by this tree if drag is enabled
31958             * @type Roo.tree.TreeDragZone
31959             */
31960             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31961                ddGroup: this.ddGroup || "TreeDD",
31962                scroll: this.ddScroll
31963            });
31964         }
31965         this.getSelectionModel().init(this);
31966         if (!this.root) {
31967             Roo.log("ROOT not set in tree");
31968             return this;
31969         }
31970         this.root.render();
31971         if(!this.rootVisible){
31972             this.root.renderChildren();
31973         }
31974         return this;
31975     }
31976 });/*
31977  * Based on:
31978  * Ext JS Library 1.1.1
31979  * Copyright(c) 2006-2007, Ext JS, LLC.
31980  *
31981  * Originally Released Under LGPL - original licence link has changed is not relivant.
31982  *
31983  * Fork - LGPL
31984  * <script type="text/javascript">
31985  */
31986  
31987
31988 /**
31989  * @class Roo.tree.DefaultSelectionModel
31990  * @extends Roo.util.Observable
31991  * The default single selection for a TreePanel.
31992  * @param {Object} cfg Configuration
31993  */
31994 Roo.tree.DefaultSelectionModel = function(cfg){
31995    this.selNode = null;
31996    
31997    
31998    
31999    this.addEvents({
32000        /**
32001         * @event selectionchange
32002         * Fires when the selected node changes
32003         * @param {DefaultSelectionModel} this
32004         * @param {TreeNode} node the new selection
32005         */
32006        "selectionchange" : true,
32007
32008        /**
32009         * @event beforeselect
32010         * Fires before the selected node changes, return false to cancel the change
32011         * @param {DefaultSelectionModel} this
32012         * @param {TreeNode} node the new selection
32013         * @param {TreeNode} node the old selection
32014         */
32015        "beforeselect" : true
32016    });
32017    
32018     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32019 };
32020
32021 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32022     init : function(tree){
32023         this.tree = tree;
32024         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32025         tree.on("click", this.onNodeClick, this);
32026     },
32027     
32028     onNodeClick : function(node, e){
32029         if (e.ctrlKey && this.selNode == node)  {
32030             this.unselect(node);
32031             return;
32032         }
32033         this.select(node);
32034     },
32035     
32036     /**
32037      * Select a node.
32038      * @param {TreeNode} node The node to select
32039      * @return {TreeNode} The selected node
32040      */
32041     select : function(node){
32042         var last = this.selNode;
32043         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32044             if(last){
32045                 last.ui.onSelectedChange(false);
32046             }
32047             this.selNode = node;
32048             node.ui.onSelectedChange(true);
32049             this.fireEvent("selectionchange", this, node, last);
32050         }
32051         return node;
32052     },
32053     
32054     /**
32055      * Deselect a node.
32056      * @param {TreeNode} node The node to unselect
32057      */
32058     unselect : function(node){
32059         if(this.selNode == node){
32060             this.clearSelections();
32061         }    
32062     },
32063     
32064     /**
32065      * Clear all selections
32066      */
32067     clearSelections : function(){
32068         var n = this.selNode;
32069         if(n){
32070             n.ui.onSelectedChange(false);
32071             this.selNode = null;
32072             this.fireEvent("selectionchange", this, null);
32073         }
32074         return n;
32075     },
32076     
32077     /**
32078      * Get the selected node
32079      * @return {TreeNode} The selected node
32080      */
32081     getSelectedNode : function(){
32082         return this.selNode;    
32083     },
32084     
32085     /**
32086      * Returns true if the node is selected
32087      * @param {TreeNode} node The node to check
32088      * @return {Boolean}
32089      */
32090     isSelected : function(node){
32091         return this.selNode == node;  
32092     },
32093
32094     /**
32095      * Selects the node above the selected node in the tree, intelligently walking the nodes
32096      * @return TreeNode The new selection
32097      */
32098     selectPrevious : function(){
32099         var s = this.selNode || this.lastSelNode;
32100         if(!s){
32101             return null;
32102         }
32103         var ps = s.previousSibling;
32104         if(ps){
32105             if(!ps.isExpanded() || ps.childNodes.length < 1){
32106                 return this.select(ps);
32107             } else{
32108                 var lc = ps.lastChild;
32109                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32110                     lc = lc.lastChild;
32111                 }
32112                 return this.select(lc);
32113             }
32114         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32115             return this.select(s.parentNode);
32116         }
32117         return null;
32118     },
32119
32120     /**
32121      * Selects the node above the selected node in the tree, intelligently walking the nodes
32122      * @return TreeNode The new selection
32123      */
32124     selectNext : function(){
32125         var s = this.selNode || this.lastSelNode;
32126         if(!s){
32127             return null;
32128         }
32129         if(s.firstChild && s.isExpanded()){
32130              return this.select(s.firstChild);
32131          }else if(s.nextSibling){
32132              return this.select(s.nextSibling);
32133          }else if(s.parentNode){
32134             var newS = null;
32135             s.parentNode.bubble(function(){
32136                 if(this.nextSibling){
32137                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32138                     return false;
32139                 }
32140             });
32141             return newS;
32142          }
32143         return null;
32144     },
32145
32146     onKeyDown : function(e){
32147         var s = this.selNode || this.lastSelNode;
32148         // undesirable, but required
32149         var sm = this;
32150         if(!s){
32151             return;
32152         }
32153         var k = e.getKey();
32154         switch(k){
32155              case e.DOWN:
32156                  e.stopEvent();
32157                  this.selectNext();
32158              break;
32159              case e.UP:
32160                  e.stopEvent();
32161                  this.selectPrevious();
32162              break;
32163              case e.RIGHT:
32164                  e.preventDefault();
32165                  if(s.hasChildNodes()){
32166                      if(!s.isExpanded()){
32167                          s.expand();
32168                      }else if(s.firstChild){
32169                          this.select(s.firstChild, e);
32170                      }
32171                  }
32172              break;
32173              case e.LEFT:
32174                  e.preventDefault();
32175                  if(s.hasChildNodes() && s.isExpanded()){
32176                      s.collapse();
32177                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32178                      this.select(s.parentNode, e);
32179                  }
32180              break;
32181         };
32182     }
32183 });
32184
32185 /**
32186  * @class Roo.tree.MultiSelectionModel
32187  * @extends Roo.util.Observable
32188  * Multi selection for a TreePanel.
32189  * @param {Object} cfg Configuration
32190  */
32191 Roo.tree.MultiSelectionModel = function(){
32192    this.selNodes = [];
32193    this.selMap = {};
32194    this.addEvents({
32195        /**
32196         * @event selectionchange
32197         * Fires when the selected nodes change
32198         * @param {MultiSelectionModel} this
32199         * @param {Array} nodes Array of the selected nodes
32200         */
32201        "selectionchange" : true
32202    });
32203    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32204    
32205 };
32206
32207 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32208     init : function(tree){
32209         this.tree = tree;
32210         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32211         tree.on("click", this.onNodeClick, this);
32212     },
32213     
32214     onNodeClick : function(node, e){
32215         this.select(node, e, e.ctrlKey);
32216     },
32217     
32218     /**
32219      * Select a node.
32220      * @param {TreeNode} node The node to select
32221      * @param {EventObject} e (optional) An event associated with the selection
32222      * @param {Boolean} keepExisting True to retain existing selections
32223      * @return {TreeNode} The selected node
32224      */
32225     select : function(node, e, keepExisting){
32226         if(keepExisting !== true){
32227             this.clearSelections(true);
32228         }
32229         if(this.isSelected(node)){
32230             this.lastSelNode = node;
32231             return node;
32232         }
32233         this.selNodes.push(node);
32234         this.selMap[node.id] = node;
32235         this.lastSelNode = node;
32236         node.ui.onSelectedChange(true);
32237         this.fireEvent("selectionchange", this, this.selNodes);
32238         return node;
32239     },
32240     
32241     /**
32242      * Deselect a node.
32243      * @param {TreeNode} node The node to unselect
32244      */
32245     unselect : function(node){
32246         if(this.selMap[node.id]){
32247             node.ui.onSelectedChange(false);
32248             var sn = this.selNodes;
32249             var index = -1;
32250             if(sn.indexOf){
32251                 index = sn.indexOf(node);
32252             }else{
32253                 for(var i = 0, len = sn.length; i < len; i++){
32254                     if(sn[i] == node){
32255                         index = i;
32256                         break;
32257                     }
32258                 }
32259             }
32260             if(index != -1){
32261                 this.selNodes.splice(index, 1);
32262             }
32263             delete this.selMap[node.id];
32264             this.fireEvent("selectionchange", this, this.selNodes);
32265         }
32266     },
32267     
32268     /**
32269      * Clear all selections
32270      */
32271     clearSelections : function(suppressEvent){
32272         var sn = this.selNodes;
32273         if(sn.length > 0){
32274             for(var i = 0, len = sn.length; i < len; i++){
32275                 sn[i].ui.onSelectedChange(false);
32276             }
32277             this.selNodes = [];
32278             this.selMap = {};
32279             if(suppressEvent !== true){
32280                 this.fireEvent("selectionchange", this, this.selNodes);
32281             }
32282         }
32283     },
32284     
32285     /**
32286      * Returns true if the node is selected
32287      * @param {TreeNode} node The node to check
32288      * @return {Boolean}
32289      */
32290     isSelected : function(node){
32291         return this.selMap[node.id] ? true : false;  
32292     },
32293     
32294     /**
32295      * Returns an array of the selected nodes
32296      * @return {Array}
32297      */
32298     getSelectedNodes : function(){
32299         return this.selNodes;    
32300     },
32301
32302     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32303
32304     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32305
32306     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32307 });/*
32308  * Based on:
32309  * Ext JS Library 1.1.1
32310  * Copyright(c) 2006-2007, Ext JS, LLC.
32311  *
32312  * Originally Released Under LGPL - original licence link has changed is not relivant.
32313  *
32314  * Fork - LGPL
32315  * <script type="text/javascript">
32316  */
32317  
32318 /**
32319  * @class Roo.tree.TreeNode
32320  * @extends Roo.data.Node
32321  * @cfg {String} text The text for this node
32322  * @cfg {Boolean} expanded true to start the node expanded
32323  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32324  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32325  * @cfg {Boolean} disabled true to start the node disabled
32326  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32327  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32328  * @cfg {String} cls A css class to be added to the node
32329  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32330  * @cfg {String} href URL of the link used for the node (defaults to #)
32331  * @cfg {String} hrefTarget target frame for the link
32332  * @cfg {String} qtip An Ext QuickTip for the node
32333  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32334  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32335  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32336  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32337  * (defaults to undefined with no checkbox rendered)
32338  * @constructor
32339  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32340  */
32341 Roo.tree.TreeNode = function(attributes){
32342     attributes = attributes || {};
32343     if(typeof attributes == "string"){
32344         attributes = {text: attributes};
32345     }
32346     this.childrenRendered = false;
32347     this.rendered = false;
32348     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32349     this.expanded = attributes.expanded === true;
32350     this.isTarget = attributes.isTarget !== false;
32351     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32352     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32353
32354     /**
32355      * Read-only. The text for this node. To change it use setText().
32356      * @type String
32357      */
32358     this.text = attributes.text;
32359     /**
32360      * True if this node is disabled.
32361      * @type Boolean
32362      */
32363     this.disabled = attributes.disabled === true;
32364
32365     this.addEvents({
32366         /**
32367         * @event textchange
32368         * Fires when the text for this node is changed
32369         * @param {Node} this This node
32370         * @param {String} text The new text
32371         * @param {String} oldText The old text
32372         */
32373         "textchange" : true,
32374         /**
32375         * @event beforeexpand
32376         * Fires before this node is expanded, return false to cancel.
32377         * @param {Node} this This node
32378         * @param {Boolean} deep
32379         * @param {Boolean} anim
32380         */
32381         "beforeexpand" : true,
32382         /**
32383         * @event beforecollapse
32384         * Fires before this node is collapsed, return false to cancel.
32385         * @param {Node} this This node
32386         * @param {Boolean} deep
32387         * @param {Boolean} anim
32388         */
32389         "beforecollapse" : true,
32390         /**
32391         * @event expand
32392         * Fires when this node is expanded
32393         * @param {Node} this This node
32394         */
32395         "expand" : true,
32396         /**
32397         * @event disabledchange
32398         * Fires when the disabled status of this node changes
32399         * @param {Node} this This node
32400         * @param {Boolean} disabled
32401         */
32402         "disabledchange" : true,
32403         /**
32404         * @event collapse
32405         * Fires when this node is collapsed
32406         * @param {Node} this This node
32407         */
32408         "collapse" : true,
32409         /**
32410         * @event beforeclick
32411         * Fires before click processing. Return false to cancel the default action.
32412         * @param {Node} this This node
32413         * @param {Roo.EventObject} e The event object
32414         */
32415         "beforeclick":true,
32416         /**
32417         * @event checkchange
32418         * Fires when a node with a checkbox's checked property changes
32419         * @param {Node} this This node
32420         * @param {Boolean} checked
32421         */
32422         "checkchange":true,
32423         /**
32424         * @event click
32425         * Fires when this node is clicked
32426         * @param {Node} this This node
32427         * @param {Roo.EventObject} e The event object
32428         */
32429         "click":true,
32430         /**
32431         * @event dblclick
32432         * Fires when this node is double clicked
32433         * @param {Node} this This node
32434         * @param {Roo.EventObject} e The event object
32435         */
32436         "dblclick":true,
32437         /**
32438         * @event contextmenu
32439         * Fires when this node is right clicked
32440         * @param {Node} this This node
32441         * @param {Roo.EventObject} e The event object
32442         */
32443         "contextmenu":true,
32444         /**
32445         * @event beforechildrenrendered
32446         * Fires right before the child nodes for this node are rendered
32447         * @param {Node} this This node
32448         */
32449         "beforechildrenrendered":true
32450     });
32451
32452     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
32453
32454     /**
32455      * Read-only. The UI for this node
32456      * @type TreeNodeUI
32457      */
32458     this.ui = new uiClass(this);
32459     
32460     // finally support items[]
32461     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
32462         return;
32463     }
32464     
32465     
32466     Roo.each(this.attributes.items, function(c) {
32467         this.appendChild(Roo.factory(c,Roo.Tree));
32468     }, this);
32469     delete this.attributes.items;
32470     
32471     
32472     
32473 };
32474 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
32475     preventHScroll: true,
32476     /**
32477      * Returns true if this node is expanded
32478      * @return {Boolean}
32479      */
32480     isExpanded : function(){
32481         return this.expanded;
32482     },
32483
32484     /**
32485      * Returns the UI object for this node
32486      * @return {TreeNodeUI}
32487      */
32488     getUI : function(){
32489         return this.ui;
32490     },
32491
32492     // private override
32493     setFirstChild : function(node){
32494         var of = this.firstChild;
32495         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
32496         if(this.childrenRendered && of && node != of){
32497             of.renderIndent(true, true);
32498         }
32499         if(this.rendered){
32500             this.renderIndent(true, true);
32501         }
32502     },
32503
32504     // private override
32505     setLastChild : function(node){
32506         var ol = this.lastChild;
32507         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
32508         if(this.childrenRendered && ol && node != ol){
32509             ol.renderIndent(true, true);
32510         }
32511         if(this.rendered){
32512             this.renderIndent(true, true);
32513         }
32514     },
32515
32516     // these methods are overridden to provide lazy rendering support
32517     // private override
32518     appendChild : function()
32519     {
32520         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
32521         if(node && this.childrenRendered){
32522             node.render();
32523         }
32524         this.ui.updateExpandIcon();
32525         return node;
32526     },
32527
32528     // private override
32529     removeChild : function(node){
32530         this.ownerTree.getSelectionModel().unselect(node);
32531         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
32532         // if it's been rendered remove dom node
32533         if(this.childrenRendered){
32534             node.ui.remove();
32535         }
32536         if(this.childNodes.length < 1){
32537             this.collapse(false, false);
32538         }else{
32539             this.ui.updateExpandIcon();
32540         }
32541         if(!this.firstChild) {
32542             this.childrenRendered = false;
32543         }
32544         return node;
32545     },
32546
32547     // private override
32548     insertBefore : function(node, refNode){
32549         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
32550         if(newNode && refNode && this.childrenRendered){
32551             node.render();
32552         }
32553         this.ui.updateExpandIcon();
32554         return newNode;
32555     },
32556
32557     /**
32558      * Sets the text for this node
32559      * @param {String} text
32560      */
32561     setText : function(text){
32562         var oldText = this.text;
32563         this.text = text;
32564         this.attributes.text = text;
32565         if(this.rendered){ // event without subscribing
32566             this.ui.onTextChange(this, text, oldText);
32567         }
32568         this.fireEvent("textchange", this, text, oldText);
32569     },
32570
32571     /**
32572      * Triggers selection of this node
32573      */
32574     select : function(){
32575         this.getOwnerTree().getSelectionModel().select(this);
32576     },
32577
32578     /**
32579      * Triggers deselection of this node
32580      */
32581     unselect : function(){
32582         this.getOwnerTree().getSelectionModel().unselect(this);
32583     },
32584
32585     /**
32586      * Returns true if this node is selected
32587      * @return {Boolean}
32588      */
32589     isSelected : function(){
32590         return this.getOwnerTree().getSelectionModel().isSelected(this);
32591     },
32592
32593     /**
32594      * Expand this node.
32595      * @param {Boolean} deep (optional) True to expand all children as well
32596      * @param {Boolean} anim (optional) false to cancel the default animation
32597      * @param {Function} callback (optional) A callback to be called when
32598      * expanding this node completes (does not wait for deep expand to complete).
32599      * Called with 1 parameter, this node.
32600      */
32601     expand : function(deep, anim, callback){
32602         if(!this.expanded){
32603             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
32604                 return;
32605             }
32606             if(!this.childrenRendered){
32607                 this.renderChildren();
32608             }
32609             this.expanded = true;
32610             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
32611                 this.ui.animExpand(function(){
32612                     this.fireEvent("expand", this);
32613                     if(typeof callback == "function"){
32614                         callback(this);
32615                     }
32616                     if(deep === true){
32617                         this.expandChildNodes(true);
32618                     }
32619                 }.createDelegate(this));
32620                 return;
32621             }else{
32622                 this.ui.expand();
32623                 this.fireEvent("expand", this);
32624                 if(typeof callback == "function"){
32625                     callback(this);
32626                 }
32627             }
32628         }else{
32629            if(typeof callback == "function"){
32630                callback(this);
32631            }
32632         }
32633         if(deep === true){
32634             this.expandChildNodes(true);
32635         }
32636     },
32637
32638     isHiddenRoot : function(){
32639         return this.isRoot && !this.getOwnerTree().rootVisible;
32640     },
32641
32642     /**
32643      * Collapse this node.
32644      * @param {Boolean} deep (optional) True to collapse all children as well
32645      * @param {Boolean} anim (optional) false to cancel the default animation
32646      */
32647     collapse : function(deep, anim){
32648         if(this.expanded && !this.isHiddenRoot()){
32649             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32650                 return;
32651             }
32652             this.expanded = false;
32653             if((this.getOwnerTree().animate && anim !== false) || anim){
32654                 this.ui.animCollapse(function(){
32655                     this.fireEvent("collapse", this);
32656                     if(deep === true){
32657                         this.collapseChildNodes(true);
32658                     }
32659                 }.createDelegate(this));
32660                 return;
32661             }else{
32662                 this.ui.collapse();
32663                 this.fireEvent("collapse", this);
32664             }
32665         }
32666         if(deep === true){
32667             var cs = this.childNodes;
32668             for(var i = 0, len = cs.length; i < len; i++) {
32669                 cs[i].collapse(true, false);
32670             }
32671         }
32672     },
32673
32674     // private
32675     delayedExpand : function(delay){
32676         if(!this.expandProcId){
32677             this.expandProcId = this.expand.defer(delay, this);
32678         }
32679     },
32680
32681     // private
32682     cancelExpand : function(){
32683         if(this.expandProcId){
32684             clearTimeout(this.expandProcId);
32685         }
32686         this.expandProcId = false;
32687     },
32688
32689     /**
32690      * Toggles expanded/collapsed state of the node
32691      */
32692     toggle : function(){
32693         if(this.expanded){
32694             this.collapse();
32695         }else{
32696             this.expand();
32697         }
32698     },
32699
32700     /**
32701      * Ensures all parent nodes are expanded
32702      */
32703     ensureVisible : function(callback){
32704         var tree = this.getOwnerTree();
32705         tree.expandPath(this.parentNode.getPath(), false, function(){
32706             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32707             Roo.callback(callback);
32708         }.createDelegate(this));
32709     },
32710
32711     /**
32712      * Expand all child nodes
32713      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32714      */
32715     expandChildNodes : function(deep){
32716         var cs = this.childNodes;
32717         for(var i = 0, len = cs.length; i < len; i++) {
32718                 cs[i].expand(deep);
32719         }
32720     },
32721
32722     /**
32723      * Collapse all child nodes
32724      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32725      */
32726     collapseChildNodes : function(deep){
32727         var cs = this.childNodes;
32728         for(var i = 0, len = cs.length; i < len; i++) {
32729                 cs[i].collapse(deep);
32730         }
32731     },
32732
32733     /**
32734      * Disables this node
32735      */
32736     disable : function(){
32737         this.disabled = true;
32738         this.unselect();
32739         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32740             this.ui.onDisableChange(this, true);
32741         }
32742         this.fireEvent("disabledchange", this, true);
32743     },
32744
32745     /**
32746      * Enables this node
32747      */
32748     enable : function(){
32749         this.disabled = false;
32750         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32751             this.ui.onDisableChange(this, false);
32752         }
32753         this.fireEvent("disabledchange", this, false);
32754     },
32755
32756     // private
32757     renderChildren : function(suppressEvent){
32758         if(suppressEvent !== false){
32759             this.fireEvent("beforechildrenrendered", this);
32760         }
32761         var cs = this.childNodes;
32762         for(var i = 0, len = cs.length; i < len; i++){
32763             cs[i].render(true);
32764         }
32765         this.childrenRendered = true;
32766     },
32767
32768     // private
32769     sort : function(fn, scope){
32770         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32771         if(this.childrenRendered){
32772             var cs = this.childNodes;
32773             for(var i = 0, len = cs.length; i < len; i++){
32774                 cs[i].render(true);
32775             }
32776         }
32777     },
32778
32779     // private
32780     render : function(bulkRender){
32781         this.ui.render(bulkRender);
32782         if(!this.rendered){
32783             this.rendered = true;
32784             if(this.expanded){
32785                 this.expanded = false;
32786                 this.expand(false, false);
32787             }
32788         }
32789     },
32790
32791     // private
32792     renderIndent : function(deep, refresh){
32793         if(refresh){
32794             this.ui.childIndent = null;
32795         }
32796         this.ui.renderIndent();
32797         if(deep === true && this.childrenRendered){
32798             var cs = this.childNodes;
32799             for(var i = 0, len = cs.length; i < len; i++){
32800                 cs[i].renderIndent(true, refresh);
32801             }
32802         }
32803     }
32804 });/*
32805  * Based on:
32806  * Ext JS Library 1.1.1
32807  * Copyright(c) 2006-2007, Ext JS, LLC.
32808  *
32809  * Originally Released Under LGPL - original licence link has changed is not relivant.
32810  *
32811  * Fork - LGPL
32812  * <script type="text/javascript">
32813  */
32814  
32815 /**
32816  * @class Roo.tree.AsyncTreeNode
32817  * @extends Roo.tree.TreeNode
32818  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32819  * @constructor
32820  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32821  */
32822  Roo.tree.AsyncTreeNode = function(config){
32823     this.loaded = false;
32824     this.loading = false;
32825     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32826     /**
32827     * @event beforeload
32828     * Fires before this node is loaded, return false to cancel
32829     * @param {Node} this This node
32830     */
32831     this.addEvents({'beforeload':true, 'load': true});
32832     /**
32833     * @event load
32834     * Fires when this node is loaded
32835     * @param {Node} this This node
32836     */
32837     /**
32838      * The loader used by this node (defaults to using the tree's defined loader)
32839      * @type TreeLoader
32840      * @property loader
32841      */
32842 };
32843 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32844     expand : function(deep, anim, callback){
32845         if(this.loading){ // if an async load is already running, waiting til it's done
32846             var timer;
32847             var f = function(){
32848                 if(!this.loading){ // done loading
32849                     clearInterval(timer);
32850                     this.expand(deep, anim, callback);
32851                 }
32852             }.createDelegate(this);
32853             timer = setInterval(f, 200);
32854             return;
32855         }
32856         if(!this.loaded){
32857             if(this.fireEvent("beforeload", this) === false){
32858                 return;
32859             }
32860             this.loading = true;
32861             this.ui.beforeLoad(this);
32862             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32863             if(loader){
32864                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32865                 return;
32866             }
32867         }
32868         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32869     },
32870     
32871     /**
32872      * Returns true if this node is currently loading
32873      * @return {Boolean}
32874      */
32875     isLoading : function(){
32876         return this.loading;  
32877     },
32878     
32879     loadComplete : function(deep, anim, callback){
32880         this.loading = false;
32881         this.loaded = true;
32882         this.ui.afterLoad(this);
32883         this.fireEvent("load", this);
32884         this.expand(deep, anim, callback);
32885     },
32886     
32887     /**
32888      * Returns true if this node has been loaded
32889      * @return {Boolean}
32890      */
32891     isLoaded : function(){
32892         return this.loaded;
32893     },
32894     
32895     hasChildNodes : function(){
32896         if(!this.isLeaf() && !this.loaded){
32897             return true;
32898         }else{
32899             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32900         }
32901     },
32902
32903     /**
32904      * Trigger a reload for this node
32905      * @param {Function} callback
32906      */
32907     reload : function(callback){
32908         this.collapse(false, false);
32909         while(this.firstChild){
32910             this.removeChild(this.firstChild);
32911         }
32912         this.childrenRendered = false;
32913         this.loaded = false;
32914         if(this.isHiddenRoot()){
32915             this.expanded = false;
32916         }
32917         this.expand(false, false, callback);
32918     }
32919 });/*
32920  * Based on:
32921  * Ext JS Library 1.1.1
32922  * Copyright(c) 2006-2007, Ext JS, LLC.
32923  *
32924  * Originally Released Under LGPL - original licence link has changed is not relivant.
32925  *
32926  * Fork - LGPL
32927  * <script type="text/javascript">
32928  */
32929  
32930 /**
32931  * @class Roo.tree.TreeNodeUI
32932  * @constructor
32933  * @param {Object} node The node to render
32934  * The TreeNode UI implementation is separate from the
32935  * tree implementation. Unless you are customizing the tree UI,
32936  * you should never have to use this directly.
32937  */
32938 Roo.tree.TreeNodeUI = function(node){
32939     this.node = node;
32940     this.rendered = false;
32941     this.animating = false;
32942     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32943 };
32944
32945 Roo.tree.TreeNodeUI.prototype = {
32946     removeChild : function(node){
32947         if(this.rendered){
32948             this.ctNode.removeChild(node.ui.getEl());
32949         }
32950     },
32951
32952     beforeLoad : function(){
32953          this.addClass("x-tree-node-loading");
32954     },
32955
32956     afterLoad : function(){
32957          this.removeClass("x-tree-node-loading");
32958     },
32959
32960     onTextChange : function(node, text, oldText){
32961         if(this.rendered){
32962             this.textNode.innerHTML = text;
32963         }
32964     },
32965
32966     onDisableChange : function(node, state){
32967         this.disabled = state;
32968         if(state){
32969             this.addClass("x-tree-node-disabled");
32970         }else{
32971             this.removeClass("x-tree-node-disabled");
32972         }
32973     },
32974
32975     onSelectedChange : function(state){
32976         if(state){
32977             this.focus();
32978             this.addClass("x-tree-selected");
32979         }else{
32980             //this.blur();
32981             this.removeClass("x-tree-selected");
32982         }
32983     },
32984
32985     onMove : function(tree, node, oldParent, newParent, index, refNode){
32986         this.childIndent = null;
32987         if(this.rendered){
32988             var targetNode = newParent.ui.getContainer();
32989             if(!targetNode){//target not rendered
32990                 this.holder = document.createElement("div");
32991                 this.holder.appendChild(this.wrap);
32992                 return;
32993             }
32994             var insertBefore = refNode ? refNode.ui.getEl() : null;
32995             if(insertBefore){
32996                 targetNode.insertBefore(this.wrap, insertBefore);
32997             }else{
32998                 targetNode.appendChild(this.wrap);
32999             }
33000             this.node.renderIndent(true);
33001         }
33002     },
33003
33004     addClass : function(cls){
33005         if(this.elNode){
33006             Roo.fly(this.elNode).addClass(cls);
33007         }
33008     },
33009
33010     removeClass : function(cls){
33011         if(this.elNode){
33012             Roo.fly(this.elNode).removeClass(cls);
33013         }
33014     },
33015
33016     remove : function(){
33017         if(this.rendered){
33018             this.holder = document.createElement("div");
33019             this.holder.appendChild(this.wrap);
33020         }
33021     },
33022
33023     fireEvent : function(){
33024         return this.node.fireEvent.apply(this.node, arguments);
33025     },
33026
33027     initEvents : function(){
33028         this.node.on("move", this.onMove, this);
33029         var E = Roo.EventManager;
33030         var a = this.anchor;
33031
33032         var el = Roo.fly(a, '_treeui');
33033
33034         if(Roo.isOpera){ // opera render bug ignores the CSS
33035             el.setStyle("text-decoration", "none");
33036         }
33037
33038         el.on("click", this.onClick, this);
33039         el.on("dblclick", this.onDblClick, this);
33040
33041         if(this.checkbox){
33042             Roo.EventManager.on(this.checkbox,
33043                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33044         }
33045
33046         el.on("contextmenu", this.onContextMenu, this);
33047
33048         var icon = Roo.fly(this.iconNode);
33049         icon.on("click", this.onClick, this);
33050         icon.on("dblclick", this.onDblClick, this);
33051         icon.on("contextmenu", this.onContextMenu, this);
33052         E.on(this.ecNode, "click", this.ecClick, this, true);
33053
33054         if(this.node.disabled){
33055             this.addClass("x-tree-node-disabled");
33056         }
33057         if(this.node.hidden){
33058             this.addClass("x-tree-node-disabled");
33059         }
33060         var ot = this.node.getOwnerTree();
33061         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33062         if(dd && (!this.node.isRoot || ot.rootVisible)){
33063             Roo.dd.Registry.register(this.elNode, {
33064                 node: this.node,
33065                 handles: this.getDDHandles(),
33066                 isHandle: false
33067             });
33068         }
33069     },
33070
33071     getDDHandles : function(){
33072         return [this.iconNode, this.textNode];
33073     },
33074
33075     hide : function(){
33076         if(this.rendered){
33077             this.wrap.style.display = "none";
33078         }
33079     },
33080
33081     show : function(){
33082         if(this.rendered){
33083             this.wrap.style.display = "";
33084         }
33085     },
33086
33087     onContextMenu : function(e){
33088         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33089             e.preventDefault();
33090             this.focus();
33091             this.fireEvent("contextmenu", this.node, e);
33092         }
33093     },
33094
33095     onClick : function(e){
33096         if(this.dropping){
33097             e.stopEvent();
33098             return;
33099         }
33100         if(this.fireEvent("beforeclick", this.node, e) !== false){
33101             if(!this.disabled && this.node.attributes.href){
33102                 this.fireEvent("click", this.node, e);
33103                 return;
33104             }
33105             e.preventDefault();
33106             if(this.disabled){
33107                 return;
33108             }
33109
33110             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33111                 this.node.toggle();
33112             }
33113
33114             this.fireEvent("click", this.node, e);
33115         }else{
33116             e.stopEvent();
33117         }
33118     },
33119
33120     onDblClick : function(e){
33121         e.preventDefault();
33122         if(this.disabled){
33123             return;
33124         }
33125         if(this.checkbox){
33126             this.toggleCheck();
33127         }
33128         if(!this.animating && this.node.hasChildNodes()){
33129             this.node.toggle();
33130         }
33131         this.fireEvent("dblclick", this.node, e);
33132     },
33133
33134     onCheckChange : function(){
33135         var checked = this.checkbox.checked;
33136         this.node.attributes.checked = checked;
33137         this.fireEvent('checkchange', this.node, checked);
33138     },
33139
33140     ecClick : function(e){
33141         if(!this.animating && this.node.hasChildNodes()){
33142             this.node.toggle();
33143         }
33144     },
33145
33146     startDrop : function(){
33147         this.dropping = true;
33148     },
33149
33150     // delayed drop so the click event doesn't get fired on a drop
33151     endDrop : function(){
33152        setTimeout(function(){
33153            this.dropping = false;
33154        }.createDelegate(this), 50);
33155     },
33156
33157     expand : function(){
33158         this.updateExpandIcon();
33159         this.ctNode.style.display = "";
33160     },
33161
33162     focus : function(){
33163         if(!this.node.preventHScroll){
33164             try{this.anchor.focus();
33165             }catch(e){}
33166         }else if(!Roo.isIE){
33167             try{
33168                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33169                 var l = noscroll.scrollLeft;
33170                 this.anchor.focus();
33171                 noscroll.scrollLeft = l;
33172             }catch(e){}
33173         }
33174     },
33175
33176     toggleCheck : function(value){
33177         var cb = this.checkbox;
33178         if(cb){
33179             cb.checked = (value === undefined ? !cb.checked : value);
33180         }
33181     },
33182
33183     blur : function(){
33184         try{
33185             this.anchor.blur();
33186         }catch(e){}
33187     },
33188
33189     animExpand : function(callback){
33190         var ct = Roo.get(this.ctNode);
33191         ct.stopFx();
33192         if(!this.node.hasChildNodes()){
33193             this.updateExpandIcon();
33194             this.ctNode.style.display = "";
33195             Roo.callback(callback);
33196             return;
33197         }
33198         this.animating = true;
33199         this.updateExpandIcon();
33200
33201         ct.slideIn('t', {
33202            callback : function(){
33203                this.animating = false;
33204                Roo.callback(callback);
33205             },
33206             scope: this,
33207             duration: this.node.ownerTree.duration || .25
33208         });
33209     },
33210
33211     highlight : function(){
33212         var tree = this.node.getOwnerTree();
33213         Roo.fly(this.wrap).highlight(
33214             tree.hlColor || "C3DAF9",
33215             {endColor: tree.hlBaseColor}
33216         );
33217     },
33218
33219     collapse : function(){
33220         this.updateExpandIcon();
33221         this.ctNode.style.display = "none";
33222     },
33223
33224     animCollapse : function(callback){
33225         var ct = Roo.get(this.ctNode);
33226         ct.enableDisplayMode('block');
33227         ct.stopFx();
33228
33229         this.animating = true;
33230         this.updateExpandIcon();
33231
33232         ct.slideOut('t', {
33233             callback : function(){
33234                this.animating = false;
33235                Roo.callback(callback);
33236             },
33237             scope: this,
33238             duration: this.node.ownerTree.duration || .25
33239         });
33240     },
33241
33242     getContainer : function(){
33243         return this.ctNode;
33244     },
33245
33246     getEl : function(){
33247         return this.wrap;
33248     },
33249
33250     appendDDGhost : function(ghostNode){
33251         ghostNode.appendChild(this.elNode.cloneNode(true));
33252     },
33253
33254     getDDRepairXY : function(){
33255         return Roo.lib.Dom.getXY(this.iconNode);
33256     },
33257
33258     onRender : function(){
33259         this.render();
33260     },
33261
33262     render : function(bulkRender){
33263         var n = this.node, a = n.attributes;
33264         var targetNode = n.parentNode ?
33265               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33266
33267         if(!this.rendered){
33268             this.rendered = true;
33269
33270             this.renderElements(n, a, targetNode, bulkRender);
33271
33272             if(a.qtip){
33273                if(this.textNode.setAttributeNS){
33274                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33275                    if(a.qtipTitle){
33276                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33277                    }
33278                }else{
33279                    this.textNode.setAttribute("ext:qtip", a.qtip);
33280                    if(a.qtipTitle){
33281                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33282                    }
33283                }
33284             }else if(a.qtipCfg){
33285                 a.qtipCfg.target = Roo.id(this.textNode);
33286                 Roo.QuickTips.register(a.qtipCfg);
33287             }
33288             this.initEvents();
33289             if(!this.node.expanded){
33290                 this.updateExpandIcon();
33291             }
33292         }else{
33293             if(bulkRender === true) {
33294                 targetNode.appendChild(this.wrap);
33295             }
33296         }
33297     },
33298
33299     renderElements : function(n, a, targetNode, bulkRender)
33300     {
33301         // add some indent caching, this helps performance when rendering a large tree
33302         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33303         var t = n.getOwnerTree();
33304         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33305         if (typeof(n.attributes.html) != 'undefined') {
33306             txt = n.attributes.html;
33307         }
33308         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33309         var cb = typeof a.checked == 'boolean';
33310         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33311         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33312             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33313             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33314             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33315             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33316             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33317              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33318                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33319             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33320             "</li>"];
33321
33322         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33323             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33324                                 n.nextSibling.ui.getEl(), buf.join(""));
33325         }else{
33326             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33327         }
33328
33329         this.elNode = this.wrap.childNodes[0];
33330         this.ctNode = this.wrap.childNodes[1];
33331         var cs = this.elNode.childNodes;
33332         this.indentNode = cs[0];
33333         this.ecNode = cs[1];
33334         this.iconNode = cs[2];
33335         var index = 3;
33336         if(cb){
33337             this.checkbox = cs[3];
33338             index++;
33339         }
33340         this.anchor = cs[index];
33341         this.textNode = cs[index].firstChild;
33342     },
33343
33344     getAnchor : function(){
33345         return this.anchor;
33346     },
33347
33348     getTextEl : function(){
33349         return this.textNode;
33350     },
33351
33352     getIconEl : function(){
33353         return this.iconNode;
33354     },
33355
33356     isChecked : function(){
33357         return this.checkbox ? this.checkbox.checked : false;
33358     },
33359
33360     updateExpandIcon : function(){
33361         if(this.rendered){
33362             var n = this.node, c1, c2;
33363             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
33364             var hasChild = n.hasChildNodes();
33365             if(hasChild){
33366                 if(n.expanded){
33367                     cls += "-minus";
33368                     c1 = "x-tree-node-collapsed";
33369                     c2 = "x-tree-node-expanded";
33370                 }else{
33371                     cls += "-plus";
33372                     c1 = "x-tree-node-expanded";
33373                     c2 = "x-tree-node-collapsed";
33374                 }
33375                 if(this.wasLeaf){
33376                     this.removeClass("x-tree-node-leaf");
33377                     this.wasLeaf = false;
33378                 }
33379                 if(this.c1 != c1 || this.c2 != c2){
33380                     Roo.fly(this.elNode).replaceClass(c1, c2);
33381                     this.c1 = c1; this.c2 = c2;
33382                 }
33383             }else{
33384                 // this changes non-leafs into leafs if they have no children.
33385                 // it's not very rational behaviour..
33386                 
33387                 if(!this.wasLeaf && this.node.leaf){
33388                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
33389                     delete this.c1;
33390                     delete this.c2;
33391                     this.wasLeaf = true;
33392                 }
33393             }
33394             var ecc = "x-tree-ec-icon "+cls;
33395             if(this.ecc != ecc){
33396                 this.ecNode.className = ecc;
33397                 this.ecc = ecc;
33398             }
33399         }
33400     },
33401
33402     getChildIndent : function(){
33403         if(!this.childIndent){
33404             var buf = [];
33405             var p = this.node;
33406             while(p){
33407                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
33408                     if(!p.isLast()) {
33409                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
33410                     } else {
33411                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
33412                     }
33413                 }
33414                 p = p.parentNode;
33415             }
33416             this.childIndent = buf.join("");
33417         }
33418         return this.childIndent;
33419     },
33420
33421     renderIndent : function(){
33422         if(this.rendered){
33423             var indent = "";
33424             var p = this.node.parentNode;
33425             if(p){
33426                 indent = p.ui.getChildIndent();
33427             }
33428             if(this.indentMarkup != indent){ // don't rerender if not required
33429                 this.indentNode.innerHTML = indent;
33430                 this.indentMarkup = indent;
33431             }
33432             this.updateExpandIcon();
33433         }
33434     }
33435 };
33436
33437 Roo.tree.RootTreeNodeUI = function(){
33438     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
33439 };
33440 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
33441     render : function(){
33442         if(!this.rendered){
33443             var targetNode = this.node.ownerTree.innerCt.dom;
33444             this.node.expanded = true;
33445             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
33446             this.wrap = this.ctNode = targetNode.firstChild;
33447         }
33448     },
33449     collapse : function(){
33450     },
33451     expand : function(){
33452     }
33453 });/*
33454  * Based on:
33455  * Ext JS Library 1.1.1
33456  * Copyright(c) 2006-2007, Ext JS, LLC.
33457  *
33458  * Originally Released Under LGPL - original licence link has changed is not relivant.
33459  *
33460  * Fork - LGPL
33461  * <script type="text/javascript">
33462  */
33463 /**
33464  * @class Roo.tree.TreeLoader
33465  * @extends Roo.util.Observable
33466  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
33467  * nodes from a specified URL. The response must be a javascript Array definition
33468  * who's elements are node definition objects. eg:
33469  * <pre><code>
33470 {  success : true,
33471    data :      [
33472    
33473     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
33474     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
33475     ]
33476 }
33477
33478
33479 </code></pre>
33480  * <br><br>
33481  * The old style respose with just an array is still supported, but not recommended.
33482  * <br><br>
33483  *
33484  * A server request is sent, and child nodes are loaded only when a node is expanded.
33485  * The loading node's id is passed to the server under the parameter name "node" to
33486  * enable the server to produce the correct child nodes.
33487  * <br><br>
33488  * To pass extra parameters, an event handler may be attached to the "beforeload"
33489  * event, and the parameters specified in the TreeLoader's baseParams property:
33490  * <pre><code>
33491     myTreeLoader.on("beforeload", function(treeLoader, node) {
33492         this.baseParams.category = node.attributes.category;
33493     }, this);
33494 </code></pre><
33495  * This would pass an HTTP parameter called "category" to the server containing
33496  * the value of the Node's "category" attribute.
33497  * @constructor
33498  * Creates a new Treeloader.
33499  * @param {Object} config A config object containing config properties.
33500  */
33501 Roo.tree.TreeLoader = function(config){
33502     this.baseParams = {};
33503     this.requestMethod = "POST";
33504     Roo.apply(this, config);
33505
33506     this.addEvents({
33507     
33508         /**
33509          * @event beforeload
33510          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
33511          * @param {Object} This TreeLoader object.
33512          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33513          * @param {Object} callback The callback function specified in the {@link #load} call.
33514          */
33515         beforeload : true,
33516         /**
33517          * @event load
33518          * Fires when the node has been successfuly loaded.
33519          * @param {Object} This TreeLoader object.
33520          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33521          * @param {Object} response The response object containing the data from the server.
33522          */
33523         load : true,
33524         /**
33525          * @event loadexception
33526          * Fires if the network request failed.
33527          * @param {Object} This TreeLoader object.
33528          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33529          * @param {Object} response The response object containing the data from the server.
33530          */
33531         loadexception : true,
33532         /**
33533          * @event create
33534          * Fires before a node is created, enabling you to return custom Node types 
33535          * @param {Object} This TreeLoader object.
33536          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
33537          */
33538         create : true
33539     });
33540
33541     Roo.tree.TreeLoader.superclass.constructor.call(this);
33542 };
33543
33544 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
33545     /**
33546     * @cfg {String} dataUrl The URL from which to request a Json string which
33547     * specifies an array of node definition object representing the child nodes
33548     * to be loaded.
33549     */
33550     /**
33551     * @cfg {String} requestMethod either GET or POST
33552     * defaults to POST (due to BC)
33553     * to be loaded.
33554     */
33555     /**
33556     * @cfg {Object} baseParams (optional) An object containing properties which
33557     * specify HTTP parameters to be passed to each request for child nodes.
33558     */
33559     /**
33560     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
33561     * created by this loader. If the attributes sent by the server have an attribute in this object,
33562     * they take priority.
33563     */
33564     /**
33565     * @cfg {Object} uiProviders (optional) An object containing properties which
33566     * 
33567     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
33568     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
33569     * <i>uiProvider</i> attribute of a returned child node is a string rather
33570     * than a reference to a TreeNodeUI implementation, this that string value
33571     * is used as a property name in the uiProviders object. You can define the provider named
33572     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
33573     */
33574     uiProviders : {},
33575
33576     /**
33577     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
33578     * child nodes before loading.
33579     */
33580     clearOnLoad : true,
33581
33582     /**
33583     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
33584     * property on loading, rather than expecting an array. (eg. more compatible to a standard
33585     * Grid query { data : [ .....] }
33586     */
33587     
33588     root : false,
33589      /**
33590     * @cfg {String} queryParam (optional) 
33591     * Name of the query as it will be passed on the querystring (defaults to 'node')
33592     * eg. the request will be ?node=[id]
33593     */
33594     
33595     
33596     queryParam: false,
33597     
33598     /**
33599      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
33600      * This is called automatically when a node is expanded, but may be used to reload
33601      * a node (or append new children if the {@link #clearOnLoad} option is false.)
33602      * @param {Roo.tree.TreeNode} node
33603      * @param {Function} callback
33604      */
33605     load : function(node, callback){
33606         if(this.clearOnLoad){
33607             while(node.firstChild){
33608                 node.removeChild(node.firstChild);
33609             }
33610         }
33611         if(node.attributes.children){ // preloaded json children
33612             var cs = node.attributes.children;
33613             for(var i = 0, len = cs.length; i < len; i++){
33614                 node.appendChild(this.createNode(cs[i]));
33615             }
33616             if(typeof callback == "function"){
33617                 callback();
33618             }
33619         }else if(this.dataUrl){
33620             this.requestData(node, callback);
33621         }
33622     },
33623
33624     getParams: function(node){
33625         var buf = [], bp = this.baseParams;
33626         for(var key in bp){
33627             if(typeof bp[key] != "function"){
33628                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33629             }
33630         }
33631         var n = this.queryParam === false ? 'node' : this.queryParam;
33632         buf.push(n + "=", encodeURIComponent(node.id));
33633         return buf.join("");
33634     },
33635
33636     requestData : function(node, callback){
33637         if(this.fireEvent("beforeload", this, node, callback) !== false){
33638             this.transId = Roo.Ajax.request({
33639                 method:this.requestMethod,
33640                 url: this.dataUrl||this.url,
33641                 success: this.handleResponse,
33642                 failure: this.handleFailure,
33643                 scope: this,
33644                 argument: {callback: callback, node: node},
33645                 params: this.getParams(node)
33646             });
33647         }else{
33648             // if the load is cancelled, make sure we notify
33649             // the node that we are done
33650             if(typeof callback == "function"){
33651                 callback();
33652             }
33653         }
33654     },
33655
33656     isLoading : function(){
33657         return this.transId ? true : false;
33658     },
33659
33660     abort : function(){
33661         if(this.isLoading()){
33662             Roo.Ajax.abort(this.transId);
33663         }
33664     },
33665
33666     // private
33667     createNode : function(attr)
33668     {
33669         // apply baseAttrs, nice idea Corey!
33670         if(this.baseAttrs){
33671             Roo.applyIf(attr, this.baseAttrs);
33672         }
33673         if(this.applyLoader !== false){
33674             attr.loader = this;
33675         }
33676         // uiProvider = depreciated..
33677         
33678         if(typeof(attr.uiProvider) == 'string'){
33679            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33680                 /**  eval:var:attr */ eval(attr.uiProvider);
33681         }
33682         if(typeof(this.uiProviders['default']) != 'undefined') {
33683             attr.uiProvider = this.uiProviders['default'];
33684         }
33685         
33686         this.fireEvent('create', this, attr);
33687         
33688         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33689         return(attr.leaf ?
33690                         new Roo.tree.TreeNode(attr) :
33691                         new Roo.tree.AsyncTreeNode(attr));
33692     },
33693
33694     processResponse : function(response, node, callback)
33695     {
33696         var json = response.responseText;
33697         try {
33698             
33699             var o = Roo.decode(json);
33700             
33701             if (this.root === false && typeof(o.success) != undefined) {
33702                 this.root = 'data'; // the default behaviour for list like data..
33703                 }
33704                 
33705             if (this.root !== false &&  !o.success) {
33706                 // it's a failure condition.
33707                 var a = response.argument;
33708                 this.fireEvent("loadexception", this, a.node, response);
33709                 Roo.log("Load failed - should have a handler really");
33710                 return;
33711             }
33712             
33713             
33714             
33715             if (this.root !== false) {
33716                  o = o[this.root];
33717             }
33718             
33719             for(var i = 0, len = o.length; i < len; i++){
33720                 var n = this.createNode(o[i]);
33721                 if(n){
33722                     node.appendChild(n);
33723                 }
33724             }
33725             if(typeof callback == "function"){
33726                 callback(this, node);
33727             }
33728         }catch(e){
33729             this.handleFailure(response);
33730         }
33731     },
33732
33733     handleResponse : function(response){
33734         this.transId = false;
33735         var a = response.argument;
33736         this.processResponse(response, a.node, a.callback);
33737         this.fireEvent("load", this, a.node, response);
33738     },
33739
33740     handleFailure : function(response)
33741     {
33742         // should handle failure better..
33743         this.transId = false;
33744         var a = response.argument;
33745         this.fireEvent("loadexception", this, a.node, response);
33746         if(typeof a.callback == "function"){
33747             a.callback(this, a.node);
33748         }
33749     }
33750 });/*
33751  * Based on:
33752  * Ext JS Library 1.1.1
33753  * Copyright(c) 2006-2007, Ext JS, LLC.
33754  *
33755  * Originally Released Under LGPL - original licence link has changed is not relivant.
33756  *
33757  * Fork - LGPL
33758  * <script type="text/javascript">
33759  */
33760
33761 /**
33762 * @class Roo.tree.TreeFilter
33763 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33764 * @param {TreePanel} tree
33765 * @param {Object} config (optional)
33766  */
33767 Roo.tree.TreeFilter = function(tree, config){
33768     this.tree = tree;
33769     this.filtered = {};
33770     Roo.apply(this, config);
33771 };
33772
33773 Roo.tree.TreeFilter.prototype = {
33774     clearBlank:false,
33775     reverse:false,
33776     autoClear:false,
33777     remove:false,
33778
33779      /**
33780      * Filter the data by a specific attribute.
33781      * @param {String/RegExp} value Either string that the attribute value
33782      * should start with or a RegExp to test against the attribute
33783      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33784      * @param {TreeNode} startNode (optional) The node to start the filter at.
33785      */
33786     filter : function(value, attr, startNode){
33787         attr = attr || "text";
33788         var f;
33789         if(typeof value == "string"){
33790             var vlen = value.length;
33791             // auto clear empty filter
33792             if(vlen == 0 && this.clearBlank){
33793                 this.clear();
33794                 return;
33795             }
33796             value = value.toLowerCase();
33797             f = function(n){
33798                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33799             };
33800         }else if(value.exec){ // regex?
33801             f = function(n){
33802                 return value.test(n.attributes[attr]);
33803             };
33804         }else{
33805             throw 'Illegal filter type, must be string or regex';
33806         }
33807         this.filterBy(f, null, startNode);
33808         },
33809
33810     /**
33811      * Filter by a function. The passed function will be called with each
33812      * node in the tree (or from the startNode). If the function returns true, the node is kept
33813      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33814      * @param {Function} fn The filter function
33815      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33816      */
33817     filterBy : function(fn, scope, startNode){
33818         startNode = startNode || this.tree.root;
33819         if(this.autoClear){
33820             this.clear();
33821         }
33822         var af = this.filtered, rv = this.reverse;
33823         var f = function(n){
33824             if(n == startNode){
33825                 return true;
33826             }
33827             if(af[n.id]){
33828                 return false;
33829             }
33830             var m = fn.call(scope || n, n);
33831             if(!m || rv){
33832                 af[n.id] = n;
33833                 n.ui.hide();
33834                 return false;
33835             }
33836             return true;
33837         };
33838         startNode.cascade(f);
33839         if(this.remove){
33840            for(var id in af){
33841                if(typeof id != "function"){
33842                    var n = af[id];
33843                    if(n && n.parentNode){
33844                        n.parentNode.removeChild(n);
33845                    }
33846                }
33847            }
33848         }
33849     },
33850
33851     /**
33852      * Clears the current filter. Note: with the "remove" option
33853      * set a filter cannot be cleared.
33854      */
33855     clear : function(){
33856         var t = this.tree;
33857         var af = this.filtered;
33858         for(var id in af){
33859             if(typeof id != "function"){
33860                 var n = af[id];
33861                 if(n){
33862                     n.ui.show();
33863                 }
33864             }
33865         }
33866         this.filtered = {};
33867     }
33868 };
33869 /*
33870  * Based on:
33871  * Ext JS Library 1.1.1
33872  * Copyright(c) 2006-2007, Ext JS, LLC.
33873  *
33874  * Originally Released Under LGPL - original licence link has changed is not relivant.
33875  *
33876  * Fork - LGPL
33877  * <script type="text/javascript">
33878  */
33879  
33880
33881 /**
33882  * @class Roo.tree.TreeSorter
33883  * Provides sorting of nodes in a TreePanel
33884  * 
33885  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33886  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33887  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33888  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33889  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33890  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33891  * @constructor
33892  * @param {TreePanel} tree
33893  * @param {Object} config
33894  */
33895 Roo.tree.TreeSorter = function(tree, config){
33896     Roo.apply(this, config);
33897     tree.on("beforechildrenrendered", this.doSort, this);
33898     tree.on("append", this.updateSort, this);
33899     tree.on("insert", this.updateSort, this);
33900     
33901     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33902     var p = this.property || "text";
33903     var sortType = this.sortType;
33904     var fs = this.folderSort;
33905     var cs = this.caseSensitive === true;
33906     var leafAttr = this.leafAttr || 'leaf';
33907
33908     this.sortFn = function(n1, n2){
33909         if(fs){
33910             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33911                 return 1;
33912             }
33913             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33914                 return -1;
33915             }
33916         }
33917         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33918         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33919         if(v1 < v2){
33920                         return dsc ? +1 : -1;
33921                 }else if(v1 > v2){
33922                         return dsc ? -1 : +1;
33923         }else{
33924                 return 0;
33925         }
33926     };
33927 };
33928
33929 Roo.tree.TreeSorter.prototype = {
33930     doSort : function(node){
33931         node.sort(this.sortFn);
33932     },
33933     
33934     compareNodes : function(n1, n2){
33935         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33936     },
33937     
33938     updateSort : function(tree, node){
33939         if(node.childrenRendered){
33940             this.doSort.defer(1, this, [node]);
33941         }
33942     }
33943 };/*
33944  * Based on:
33945  * Ext JS Library 1.1.1
33946  * Copyright(c) 2006-2007, Ext JS, LLC.
33947  *
33948  * Originally Released Under LGPL - original licence link has changed is not relivant.
33949  *
33950  * Fork - LGPL
33951  * <script type="text/javascript">
33952  */
33953
33954 if(Roo.dd.DropZone){
33955     
33956 Roo.tree.TreeDropZone = function(tree, config){
33957     this.allowParentInsert = false;
33958     this.allowContainerDrop = false;
33959     this.appendOnly = false;
33960     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33961     this.tree = tree;
33962     this.lastInsertClass = "x-tree-no-status";
33963     this.dragOverData = {};
33964 };
33965
33966 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33967     ddGroup : "TreeDD",
33968     scroll:  true,
33969     
33970     expandDelay : 1000,
33971     
33972     expandNode : function(node){
33973         if(node.hasChildNodes() && !node.isExpanded()){
33974             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33975         }
33976     },
33977     
33978     queueExpand : function(node){
33979         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33980     },
33981     
33982     cancelExpand : function(){
33983         if(this.expandProcId){
33984             clearTimeout(this.expandProcId);
33985             this.expandProcId = false;
33986         }
33987     },
33988     
33989     isValidDropPoint : function(n, pt, dd, e, data){
33990         if(!n || !data){ return false; }
33991         var targetNode = n.node;
33992         var dropNode = data.node;
33993         // default drop rules
33994         if(!(targetNode && targetNode.isTarget && pt)){
33995             return false;
33996         }
33997         if(pt == "append" && targetNode.allowChildren === false){
33998             return false;
33999         }
34000         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34001             return false;
34002         }
34003         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34004             return false;
34005         }
34006         // reuse the object
34007         var overEvent = this.dragOverData;
34008         overEvent.tree = this.tree;
34009         overEvent.target = targetNode;
34010         overEvent.data = data;
34011         overEvent.point = pt;
34012         overEvent.source = dd;
34013         overEvent.rawEvent = e;
34014         overEvent.dropNode = dropNode;
34015         overEvent.cancel = false;  
34016         var result = this.tree.fireEvent("nodedragover", overEvent);
34017         return overEvent.cancel === false && result !== false;
34018     },
34019     
34020     getDropPoint : function(e, n, dd)
34021     {
34022         var tn = n.node;
34023         if(tn.isRoot){
34024             return tn.allowChildren !== false ? "append" : false; // always append for root
34025         }
34026         var dragEl = n.ddel;
34027         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34028         var y = Roo.lib.Event.getPageY(e);
34029         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34030         
34031         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34032         var noAppend = tn.allowChildren === false;
34033         if(this.appendOnly || tn.parentNode.allowChildren === false){
34034             return noAppend ? false : "append";
34035         }
34036         var noBelow = false;
34037         if(!this.allowParentInsert){
34038             noBelow = tn.hasChildNodes() && tn.isExpanded();
34039         }
34040         var q = (b - t) / (noAppend ? 2 : 3);
34041         if(y >= t && y < (t + q)){
34042             return "above";
34043         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34044             return "below";
34045         }else{
34046             return "append";
34047         }
34048     },
34049     
34050     onNodeEnter : function(n, dd, e, data)
34051     {
34052         this.cancelExpand();
34053     },
34054     
34055     onNodeOver : function(n, dd, e, data)
34056     {
34057        
34058         var pt = this.getDropPoint(e, n, dd);
34059         var node = n.node;
34060         
34061         // auto node expand check
34062         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34063             this.queueExpand(node);
34064         }else if(pt != "append"){
34065             this.cancelExpand();
34066         }
34067         
34068         // set the insert point style on the target node
34069         var returnCls = this.dropNotAllowed;
34070         if(this.isValidDropPoint(n, pt, dd, e, data)){
34071            if(pt){
34072                var el = n.ddel;
34073                var cls;
34074                if(pt == "above"){
34075                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34076                    cls = "x-tree-drag-insert-above";
34077                }else if(pt == "below"){
34078                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34079                    cls = "x-tree-drag-insert-below";
34080                }else{
34081                    returnCls = "x-tree-drop-ok-append";
34082                    cls = "x-tree-drag-append";
34083                }
34084                if(this.lastInsertClass != cls){
34085                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34086                    this.lastInsertClass = cls;
34087                }
34088            }
34089        }
34090        return returnCls;
34091     },
34092     
34093     onNodeOut : function(n, dd, e, data){
34094         
34095         this.cancelExpand();
34096         this.removeDropIndicators(n);
34097     },
34098     
34099     onNodeDrop : function(n, dd, e, data){
34100         var point = this.getDropPoint(e, n, dd);
34101         var targetNode = n.node;
34102         targetNode.ui.startDrop();
34103         if(!this.isValidDropPoint(n, point, dd, e, data)){
34104             targetNode.ui.endDrop();
34105             return false;
34106         }
34107         // first try to find the drop node
34108         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34109         var dropEvent = {
34110             tree : this.tree,
34111             target: targetNode,
34112             data: data,
34113             point: point,
34114             source: dd,
34115             rawEvent: e,
34116             dropNode: dropNode,
34117             cancel: !dropNode   
34118         };
34119         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34120         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34121             targetNode.ui.endDrop();
34122             return false;
34123         }
34124         // allow target changing
34125         targetNode = dropEvent.target;
34126         if(point == "append" && !targetNode.isExpanded()){
34127             targetNode.expand(false, null, function(){
34128                 this.completeDrop(dropEvent);
34129             }.createDelegate(this));
34130         }else{
34131             this.completeDrop(dropEvent);
34132         }
34133         return true;
34134     },
34135     
34136     completeDrop : function(de){
34137         var ns = de.dropNode, p = de.point, t = de.target;
34138         if(!(ns instanceof Array)){
34139             ns = [ns];
34140         }
34141         var n;
34142         for(var i = 0, len = ns.length; i < len; i++){
34143             n = ns[i];
34144             if(p == "above"){
34145                 t.parentNode.insertBefore(n, t);
34146             }else if(p == "below"){
34147                 t.parentNode.insertBefore(n, t.nextSibling);
34148             }else{
34149                 t.appendChild(n);
34150             }
34151         }
34152         n.ui.focus();
34153         if(this.tree.hlDrop){
34154             n.ui.highlight();
34155         }
34156         t.ui.endDrop();
34157         this.tree.fireEvent("nodedrop", de);
34158     },
34159     
34160     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34161         if(this.tree.hlDrop){
34162             dropNode.ui.focus();
34163             dropNode.ui.highlight();
34164         }
34165         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34166     },
34167     
34168     getTree : function(){
34169         return this.tree;
34170     },
34171     
34172     removeDropIndicators : function(n){
34173         if(n && n.ddel){
34174             var el = n.ddel;
34175             Roo.fly(el).removeClass([
34176                     "x-tree-drag-insert-above",
34177                     "x-tree-drag-insert-below",
34178                     "x-tree-drag-append"]);
34179             this.lastInsertClass = "_noclass";
34180         }
34181     },
34182     
34183     beforeDragDrop : function(target, e, id){
34184         this.cancelExpand();
34185         return true;
34186     },
34187     
34188     afterRepair : function(data){
34189         if(data && Roo.enableFx){
34190             data.node.ui.highlight();
34191         }
34192         this.hideProxy();
34193     } 
34194     
34195 });
34196
34197 }
34198 /*
34199  * Based on:
34200  * Ext JS Library 1.1.1
34201  * Copyright(c) 2006-2007, Ext JS, LLC.
34202  *
34203  * Originally Released Under LGPL - original licence link has changed is not relivant.
34204  *
34205  * Fork - LGPL
34206  * <script type="text/javascript">
34207  */
34208  
34209
34210 if(Roo.dd.DragZone){
34211 Roo.tree.TreeDragZone = function(tree, config){
34212     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34213     this.tree = tree;
34214 };
34215
34216 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34217     ddGroup : "TreeDD",
34218    
34219     onBeforeDrag : function(data, e){
34220         var n = data.node;
34221         return n && n.draggable && !n.disabled;
34222     },
34223      
34224     
34225     onInitDrag : function(e){
34226         var data = this.dragData;
34227         this.tree.getSelectionModel().select(data.node);
34228         this.proxy.update("");
34229         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34230         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34231     },
34232     
34233     getRepairXY : function(e, data){
34234         return data.node.ui.getDDRepairXY();
34235     },
34236     
34237     onEndDrag : function(data, e){
34238         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34239         
34240         
34241     },
34242     
34243     onValidDrop : function(dd, e, id){
34244         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34245         this.hideProxy();
34246     },
34247     
34248     beforeInvalidDrop : function(e, id){
34249         // this scrolls the original position back into view
34250         var sm = this.tree.getSelectionModel();
34251         sm.clearSelections();
34252         sm.select(this.dragData.node);
34253     }
34254 });
34255 }/*
34256  * Based on:
34257  * Ext JS Library 1.1.1
34258  * Copyright(c) 2006-2007, Ext JS, LLC.
34259  *
34260  * Originally Released Under LGPL - original licence link has changed is not relivant.
34261  *
34262  * Fork - LGPL
34263  * <script type="text/javascript">
34264  */
34265 /**
34266  * @class Roo.tree.TreeEditor
34267  * @extends Roo.Editor
34268  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34269  * as the editor field.
34270  * @constructor
34271  * @param {Object} config (used to be the tree panel.)
34272  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34273  * 
34274  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34275  * @cfg {Roo.form.TextField|Object} field The field configuration
34276  *
34277  * 
34278  */
34279 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34280     var tree = config;
34281     var field;
34282     if (oldconfig) { // old style..
34283         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34284     } else {
34285         // new style..
34286         tree = config.tree;
34287         config.field = config.field  || {};
34288         config.field.xtype = 'TextField';
34289         field = Roo.factory(config.field, Roo.form);
34290     }
34291     config = config || {};
34292     
34293     
34294     this.addEvents({
34295         /**
34296          * @event beforenodeedit
34297          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34298          * false from the handler of this event.
34299          * @param {Editor} this
34300          * @param {Roo.tree.Node} node 
34301          */
34302         "beforenodeedit" : true
34303     });
34304     
34305     //Roo.log(config);
34306     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34307
34308     this.tree = tree;
34309
34310     tree.on('beforeclick', this.beforeNodeClick, this);
34311     tree.getTreeEl().on('mousedown', this.hide, this);
34312     this.on('complete', this.updateNode, this);
34313     this.on('beforestartedit', this.fitToTree, this);
34314     this.on('startedit', this.bindScroll, this, {delay:10});
34315     this.on('specialkey', this.onSpecialKey, this);
34316 };
34317
34318 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34319     /**
34320      * @cfg {String} alignment
34321      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34322      */
34323     alignment: "l-l",
34324     // inherit
34325     autoSize: false,
34326     /**
34327      * @cfg {Boolean} hideEl
34328      * True to hide the bound element while the editor is displayed (defaults to false)
34329      */
34330     hideEl : false,
34331     /**
34332      * @cfg {String} cls
34333      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34334      */
34335     cls: "x-small-editor x-tree-editor",
34336     /**
34337      * @cfg {Boolean} shim
34338      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34339      */
34340     shim:false,
34341     // inherit
34342     shadow:"frame",
34343     /**
34344      * @cfg {Number} maxWidth
34345      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34346      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34347      * scroll and client offsets into account prior to each edit.
34348      */
34349     maxWidth: 250,
34350
34351     editDelay : 350,
34352
34353     // private
34354     fitToTree : function(ed, el){
34355         var td = this.tree.getTreeEl().dom, nd = el.dom;
34356         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34357             td.scrollLeft = nd.offsetLeft;
34358         }
34359         var w = Math.min(
34360                 this.maxWidth,
34361                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
34362         this.setSize(w, '');
34363         
34364         return this.fireEvent('beforenodeedit', this, this.editNode);
34365         
34366     },
34367
34368     // private
34369     triggerEdit : function(node){
34370         this.completeEdit();
34371         this.editNode = node;
34372         this.startEdit(node.ui.textNode, node.text);
34373     },
34374
34375     // private
34376     bindScroll : function(){
34377         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
34378     },
34379
34380     // private
34381     beforeNodeClick : function(node, e){
34382         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
34383         this.lastClick = new Date();
34384         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
34385             e.stopEvent();
34386             this.triggerEdit(node);
34387             return false;
34388         }
34389         return true;
34390     },
34391
34392     // private
34393     updateNode : function(ed, value){
34394         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
34395         this.editNode.setText(value);
34396     },
34397
34398     // private
34399     onHide : function(){
34400         Roo.tree.TreeEditor.superclass.onHide.call(this);
34401         if(this.editNode){
34402             this.editNode.ui.focus();
34403         }
34404     },
34405
34406     // private
34407     onSpecialKey : function(field, e){
34408         var k = e.getKey();
34409         if(k == e.ESC){
34410             e.stopEvent();
34411             this.cancelEdit();
34412         }else if(k == e.ENTER && !e.hasModifier()){
34413             e.stopEvent();
34414             this.completeEdit();
34415         }
34416     }
34417 });//<Script type="text/javascript">
34418 /*
34419  * Based on:
34420  * Ext JS Library 1.1.1
34421  * Copyright(c) 2006-2007, Ext JS, LLC.
34422  *
34423  * Originally Released Under LGPL - original licence link has changed is not relivant.
34424  *
34425  * Fork - LGPL
34426  * <script type="text/javascript">
34427  */
34428  
34429 /**
34430  * Not documented??? - probably should be...
34431  */
34432
34433 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
34434     //focus: Roo.emptyFn, // prevent odd scrolling behavior
34435     
34436     renderElements : function(n, a, targetNode, bulkRender){
34437         //consel.log("renderElements?");
34438         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34439
34440         var t = n.getOwnerTree();
34441         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
34442         
34443         var cols = t.columns;
34444         var bw = t.borderWidth;
34445         var c = cols[0];
34446         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34447          var cb = typeof a.checked == "boolean";
34448         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34449         var colcls = 'x-t-' + tid + '-c0';
34450         var buf = [
34451             '<li class="x-tree-node">',
34452             
34453                 
34454                 '<div class="x-tree-node-el ', a.cls,'">',
34455                     // extran...
34456                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
34457                 
34458                 
34459                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
34460                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
34461                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
34462                            (a.icon ? ' x-tree-node-inline-icon' : ''),
34463                            (a.iconCls ? ' '+a.iconCls : ''),
34464                            '" unselectable="on" />',
34465                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
34466                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
34467                              
34468                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34469                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
34470                             '<span unselectable="on" qtip="' + tx + '">',
34471                              tx,
34472                              '</span></a>' ,
34473                     '</div>',
34474                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34475                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
34476                  ];
34477         for(var i = 1, len = cols.length; i < len; i++){
34478             c = cols[i];
34479             colcls = 'x-t-' + tid + '-c' +i;
34480             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34481             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
34482                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
34483                       "</div>");
34484          }
34485          
34486          buf.push(
34487             '</a>',
34488             '<div class="x-clear"></div></div>',
34489             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34490             "</li>");
34491         
34492         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34493             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34494                                 n.nextSibling.ui.getEl(), buf.join(""));
34495         }else{
34496             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34497         }
34498         var el = this.wrap.firstChild;
34499         this.elRow = el;
34500         this.elNode = el.firstChild;
34501         this.ranchor = el.childNodes[1];
34502         this.ctNode = this.wrap.childNodes[1];
34503         var cs = el.firstChild.childNodes;
34504         this.indentNode = cs[0];
34505         this.ecNode = cs[1];
34506         this.iconNode = cs[2];
34507         var index = 3;
34508         if(cb){
34509             this.checkbox = cs[3];
34510             index++;
34511         }
34512         this.anchor = cs[index];
34513         
34514         this.textNode = cs[index].firstChild;
34515         
34516         //el.on("click", this.onClick, this);
34517         //el.on("dblclick", this.onDblClick, this);
34518         
34519         
34520        // console.log(this);
34521     },
34522     initEvents : function(){
34523         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
34524         
34525             
34526         var a = this.ranchor;
34527
34528         var el = Roo.get(a);
34529
34530         if(Roo.isOpera){ // opera render bug ignores the CSS
34531             el.setStyle("text-decoration", "none");
34532         }
34533
34534         el.on("click", this.onClick, this);
34535         el.on("dblclick", this.onDblClick, this);
34536         el.on("contextmenu", this.onContextMenu, this);
34537         
34538     },
34539     
34540     /*onSelectedChange : function(state){
34541         if(state){
34542             this.focus();
34543             this.addClass("x-tree-selected");
34544         }else{
34545             //this.blur();
34546             this.removeClass("x-tree-selected");
34547         }
34548     },*/
34549     addClass : function(cls){
34550         if(this.elRow){
34551             Roo.fly(this.elRow).addClass(cls);
34552         }
34553         
34554     },
34555     
34556     
34557     removeClass : function(cls){
34558         if(this.elRow){
34559             Roo.fly(this.elRow).removeClass(cls);
34560         }
34561     }
34562
34563     
34564     
34565 });//<Script type="text/javascript">
34566
34567 /*
34568  * Based on:
34569  * Ext JS Library 1.1.1
34570  * Copyright(c) 2006-2007, Ext JS, LLC.
34571  *
34572  * Originally Released Under LGPL - original licence link has changed is not relivant.
34573  *
34574  * Fork - LGPL
34575  * <script type="text/javascript">
34576  */
34577  
34578
34579 /**
34580  * @class Roo.tree.ColumnTree
34581  * @extends Roo.data.TreePanel
34582  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
34583  * @cfg {int} borderWidth  compined right/left border allowance
34584  * @constructor
34585  * @param {String/HTMLElement/Element} el The container element
34586  * @param {Object} config
34587  */
34588 Roo.tree.ColumnTree =  function(el, config)
34589 {
34590    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
34591    this.addEvents({
34592         /**
34593         * @event resize
34594         * Fire this event on a container when it resizes
34595         * @param {int} w Width
34596         * @param {int} h Height
34597         */
34598        "resize" : true
34599     });
34600     this.on('resize', this.onResize, this);
34601 };
34602
34603 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
34604     //lines:false,
34605     
34606     
34607     borderWidth: Roo.isBorderBox ? 0 : 2, 
34608     headEls : false,
34609     
34610     render : function(){
34611         // add the header.....
34612        
34613         Roo.tree.ColumnTree.superclass.render.apply(this);
34614         
34615         this.el.addClass('x-column-tree');
34616         
34617         this.headers = this.el.createChild(
34618             {cls:'x-tree-headers'},this.innerCt.dom);
34619    
34620         var cols = this.columns, c;
34621         var totalWidth = 0;
34622         this.headEls = [];
34623         var  len = cols.length;
34624         for(var i = 0; i < len; i++){
34625              c = cols[i];
34626              totalWidth += c.width;
34627             this.headEls.push(this.headers.createChild({
34628                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
34629                  cn: {
34630                      cls:'x-tree-hd-text',
34631                      html: c.header
34632                  },
34633                  style:'width:'+(c.width-this.borderWidth)+'px;'
34634              }));
34635         }
34636         this.headers.createChild({cls:'x-clear'});
34637         // prevent floats from wrapping when clipped
34638         this.headers.setWidth(totalWidth);
34639         //this.innerCt.setWidth(totalWidth);
34640         this.innerCt.setStyle({ overflow: 'auto' });
34641         this.onResize(this.width, this.height);
34642              
34643         
34644     },
34645     onResize : function(w,h)
34646     {
34647         this.height = h;
34648         this.width = w;
34649         // resize cols..
34650         this.innerCt.setWidth(this.width);
34651         this.innerCt.setHeight(this.height-20);
34652         
34653         // headers...
34654         var cols = this.columns, c;
34655         var totalWidth = 0;
34656         var expEl = false;
34657         var len = cols.length;
34658         for(var i = 0; i < len; i++){
34659             c = cols[i];
34660             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34661                 // it's the expander..
34662                 expEl  = this.headEls[i];
34663                 continue;
34664             }
34665             totalWidth += c.width;
34666             
34667         }
34668         if (expEl) {
34669             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34670         }
34671         this.headers.setWidth(w-20);
34672
34673         
34674         
34675         
34676     }
34677 });
34678 /*
34679  * Based on:
34680  * Ext JS Library 1.1.1
34681  * Copyright(c) 2006-2007, Ext JS, LLC.
34682  *
34683  * Originally Released Under LGPL - original licence link has changed is not relivant.
34684  *
34685  * Fork - LGPL
34686  * <script type="text/javascript">
34687  */
34688  
34689 /**
34690  * @class Roo.menu.Menu
34691  * @extends Roo.util.Observable
34692  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34693  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34694  * @constructor
34695  * Creates a new Menu
34696  * @param {Object} config Configuration options
34697  */
34698 Roo.menu.Menu = function(config){
34699     Roo.apply(this, config);
34700     this.id = this.id || Roo.id();
34701     this.addEvents({
34702         /**
34703          * @event beforeshow
34704          * Fires before this menu is displayed
34705          * @param {Roo.menu.Menu} this
34706          */
34707         beforeshow : true,
34708         /**
34709          * @event beforehide
34710          * Fires before this menu is hidden
34711          * @param {Roo.menu.Menu} this
34712          */
34713         beforehide : true,
34714         /**
34715          * @event show
34716          * Fires after this menu is displayed
34717          * @param {Roo.menu.Menu} this
34718          */
34719         show : true,
34720         /**
34721          * @event hide
34722          * Fires after this menu is hidden
34723          * @param {Roo.menu.Menu} this
34724          */
34725         hide : true,
34726         /**
34727          * @event click
34728          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34729          * @param {Roo.menu.Menu} this
34730          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34731          * @param {Roo.EventObject} e
34732          */
34733         click : true,
34734         /**
34735          * @event mouseover
34736          * Fires when the mouse is hovering over this menu
34737          * @param {Roo.menu.Menu} this
34738          * @param {Roo.EventObject} e
34739          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34740          */
34741         mouseover : true,
34742         /**
34743          * @event mouseout
34744          * Fires when the mouse exits this menu
34745          * @param {Roo.menu.Menu} this
34746          * @param {Roo.EventObject} e
34747          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34748          */
34749         mouseout : true,
34750         /**
34751          * @event itemclick
34752          * Fires when a menu item contained in this menu is clicked
34753          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34754          * @param {Roo.EventObject} e
34755          */
34756         itemclick: true
34757     });
34758     if (this.registerMenu) {
34759         Roo.menu.MenuMgr.register(this);
34760     }
34761     
34762     var mis = this.items;
34763     this.items = new Roo.util.MixedCollection();
34764     if(mis){
34765         this.add.apply(this, mis);
34766     }
34767 };
34768
34769 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34770     /**
34771      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34772      */
34773     minWidth : 120,
34774     /**
34775      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34776      * for bottom-right shadow (defaults to "sides")
34777      */
34778     shadow : "sides",
34779     /**
34780      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34781      * this menu (defaults to "tl-tr?")
34782      */
34783     subMenuAlign : "tl-tr?",
34784     /**
34785      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34786      * relative to its element of origin (defaults to "tl-bl?")
34787      */
34788     defaultAlign : "tl-bl?",
34789     /**
34790      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34791      */
34792     allowOtherMenus : false,
34793     /**
34794      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34795      */
34796     registerMenu : true,
34797
34798     hidden:true,
34799
34800     // private
34801     render : function(){
34802         if(this.el){
34803             return;
34804         }
34805         var el = this.el = new Roo.Layer({
34806             cls: "x-menu",
34807             shadow:this.shadow,
34808             constrain: false,
34809             parentEl: this.parentEl || document.body,
34810             zindex:15000
34811         });
34812
34813         this.keyNav = new Roo.menu.MenuNav(this);
34814
34815         if(this.plain){
34816             el.addClass("x-menu-plain");
34817         }
34818         if(this.cls){
34819             el.addClass(this.cls);
34820         }
34821         // generic focus element
34822         this.focusEl = el.createChild({
34823             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34824         });
34825         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34826         ul.on("click", this.onClick, this);
34827         ul.on("mouseover", this.onMouseOver, this);
34828         ul.on("mouseout", this.onMouseOut, this);
34829         this.items.each(function(item){
34830             var li = document.createElement("li");
34831             li.className = "x-menu-list-item";
34832             ul.dom.appendChild(li);
34833             item.render(li, this);
34834         }, this);
34835         this.ul = ul;
34836         this.autoWidth();
34837     },
34838
34839     // private
34840     autoWidth : function(){
34841         var el = this.el, ul = this.ul;
34842         if(!el){
34843             return;
34844         }
34845         var w = this.width;
34846         if(w){
34847             el.setWidth(w);
34848         }else if(Roo.isIE){
34849             el.setWidth(this.minWidth);
34850             var t = el.dom.offsetWidth; // force recalc
34851             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34852         }
34853     },
34854
34855     // private
34856     delayAutoWidth : function(){
34857         if(this.rendered){
34858             if(!this.awTask){
34859                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34860             }
34861             this.awTask.delay(20);
34862         }
34863     },
34864
34865     // private
34866     findTargetItem : function(e){
34867         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34868         if(t && t.menuItemId){
34869             return this.items.get(t.menuItemId);
34870         }
34871     },
34872
34873     // private
34874     onClick : function(e){
34875         var t;
34876         if(t = this.findTargetItem(e)){
34877             t.onClick(e);
34878             this.fireEvent("click", this, t, e);
34879         }
34880     },
34881
34882     // private
34883     setActiveItem : function(item, autoExpand){
34884         if(item != this.activeItem){
34885             if(this.activeItem){
34886                 this.activeItem.deactivate();
34887             }
34888             this.activeItem = item;
34889             item.activate(autoExpand);
34890         }else if(autoExpand){
34891             item.expandMenu();
34892         }
34893     },
34894
34895     // private
34896     tryActivate : function(start, step){
34897         var items = this.items;
34898         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34899             var item = items.get(i);
34900             if(!item.disabled && item.canActivate){
34901                 this.setActiveItem(item, false);
34902                 return item;
34903             }
34904         }
34905         return false;
34906     },
34907
34908     // private
34909     onMouseOver : function(e){
34910         var t;
34911         if(t = this.findTargetItem(e)){
34912             if(t.canActivate && !t.disabled){
34913                 this.setActiveItem(t, true);
34914             }
34915         }
34916         this.fireEvent("mouseover", this, e, t);
34917     },
34918
34919     // private
34920     onMouseOut : function(e){
34921         var t;
34922         if(t = this.findTargetItem(e)){
34923             if(t == this.activeItem && t.shouldDeactivate(e)){
34924                 this.activeItem.deactivate();
34925                 delete this.activeItem;
34926             }
34927         }
34928         this.fireEvent("mouseout", this, e, t);
34929     },
34930
34931     /**
34932      * Read-only.  Returns true if the menu is currently displayed, else false.
34933      * @type Boolean
34934      */
34935     isVisible : function(){
34936         return this.el && !this.hidden;
34937     },
34938
34939     /**
34940      * Displays this menu relative to another element
34941      * @param {String/HTMLElement/Roo.Element} element The element to align to
34942      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34943      * the element (defaults to this.defaultAlign)
34944      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34945      */
34946     show : function(el, pos, parentMenu){
34947         this.parentMenu = parentMenu;
34948         if(!this.el){
34949             this.render();
34950         }
34951         this.fireEvent("beforeshow", this);
34952         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34953     },
34954
34955     /**
34956      * Displays this menu at a specific xy position
34957      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34958      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34959      */
34960     showAt : function(xy, parentMenu, /* private: */_e){
34961         this.parentMenu = parentMenu;
34962         if(!this.el){
34963             this.render();
34964         }
34965         if(_e !== false){
34966             this.fireEvent("beforeshow", this);
34967             xy = this.el.adjustForConstraints(xy);
34968         }
34969         this.el.setXY(xy);
34970         this.el.show();
34971         this.hidden = false;
34972         this.focus();
34973         this.fireEvent("show", this);
34974     },
34975
34976     focus : function(){
34977         if(!this.hidden){
34978             this.doFocus.defer(50, this);
34979         }
34980     },
34981
34982     doFocus : function(){
34983         if(!this.hidden){
34984             this.focusEl.focus();
34985         }
34986     },
34987
34988     /**
34989      * Hides this menu and optionally all parent menus
34990      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34991      */
34992     hide : function(deep){
34993         if(this.el && this.isVisible()){
34994             this.fireEvent("beforehide", this);
34995             if(this.activeItem){
34996                 this.activeItem.deactivate();
34997                 this.activeItem = null;
34998             }
34999             this.el.hide();
35000             this.hidden = true;
35001             this.fireEvent("hide", this);
35002         }
35003         if(deep === true && this.parentMenu){
35004             this.parentMenu.hide(true);
35005         }
35006     },
35007
35008     /**
35009      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35010      * Any of the following are valid:
35011      * <ul>
35012      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35013      * <li>An HTMLElement object which will be converted to a menu item</li>
35014      * <li>A menu item config object that will be created as a new menu item</li>
35015      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35016      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35017      * </ul>
35018      * Usage:
35019      * <pre><code>
35020 // Create the menu
35021 var menu = new Roo.menu.Menu();
35022
35023 // Create a menu item to add by reference
35024 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35025
35026 // Add a bunch of items at once using different methods.
35027 // Only the last item added will be returned.
35028 var item = menu.add(
35029     menuItem,                // add existing item by ref
35030     'Dynamic Item',          // new TextItem
35031     '-',                     // new separator
35032     { text: 'Config Item' }  // new item by config
35033 );
35034 </code></pre>
35035      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35036      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35037      */
35038     add : function(){
35039         var a = arguments, l = a.length, item;
35040         for(var i = 0; i < l; i++){
35041             var el = a[i];
35042             if ((typeof(el) == "object") && el.xtype && el.xns) {
35043                 el = Roo.factory(el, Roo.menu);
35044             }
35045             
35046             if(el.render){ // some kind of Item
35047                 item = this.addItem(el);
35048             }else if(typeof el == "string"){ // string
35049                 if(el == "separator" || el == "-"){
35050                     item = this.addSeparator();
35051                 }else{
35052                     item = this.addText(el);
35053                 }
35054             }else if(el.tagName || el.el){ // element
35055                 item = this.addElement(el);
35056             }else if(typeof el == "object"){ // must be menu item config?
35057                 item = this.addMenuItem(el);
35058             }
35059         }
35060         return item;
35061     },
35062
35063     /**
35064      * Returns this menu's underlying {@link Roo.Element} object
35065      * @return {Roo.Element} The element
35066      */
35067     getEl : function(){
35068         if(!this.el){
35069             this.render();
35070         }
35071         return this.el;
35072     },
35073
35074     /**
35075      * Adds a separator bar to the menu
35076      * @return {Roo.menu.Item} The menu item that was added
35077      */
35078     addSeparator : function(){
35079         return this.addItem(new Roo.menu.Separator());
35080     },
35081
35082     /**
35083      * Adds an {@link Roo.Element} object to the menu
35084      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35085      * @return {Roo.menu.Item} The menu item that was added
35086      */
35087     addElement : function(el){
35088         return this.addItem(new Roo.menu.BaseItem(el));
35089     },
35090
35091     /**
35092      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35093      * @param {Roo.menu.Item} item The menu item to add
35094      * @return {Roo.menu.Item} The menu item that was added
35095      */
35096     addItem : function(item){
35097         this.items.add(item);
35098         if(this.ul){
35099             var li = document.createElement("li");
35100             li.className = "x-menu-list-item";
35101             this.ul.dom.appendChild(li);
35102             item.render(li, this);
35103             this.delayAutoWidth();
35104         }
35105         return item;
35106     },
35107
35108     /**
35109      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35110      * @param {Object} config A MenuItem config object
35111      * @return {Roo.menu.Item} The menu item that was added
35112      */
35113     addMenuItem : function(config){
35114         if(!(config instanceof Roo.menu.Item)){
35115             if(typeof config.checked == "boolean"){ // must be check menu item config?
35116                 config = new Roo.menu.CheckItem(config);
35117             }else{
35118                 config = new Roo.menu.Item(config);
35119             }
35120         }
35121         return this.addItem(config);
35122     },
35123
35124     /**
35125      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35126      * @param {String} text The text to display in the menu item
35127      * @return {Roo.menu.Item} The menu item that was added
35128      */
35129     addText : function(text){
35130         return this.addItem(new Roo.menu.TextItem({ text : text }));
35131     },
35132
35133     /**
35134      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35135      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35136      * @param {Roo.menu.Item} item The menu item to add
35137      * @return {Roo.menu.Item} The menu item that was added
35138      */
35139     insert : function(index, item){
35140         this.items.insert(index, item);
35141         if(this.ul){
35142             var li = document.createElement("li");
35143             li.className = "x-menu-list-item";
35144             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35145             item.render(li, this);
35146             this.delayAutoWidth();
35147         }
35148         return item;
35149     },
35150
35151     /**
35152      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35153      * @param {Roo.menu.Item} item The menu item to remove
35154      */
35155     remove : function(item){
35156         this.items.removeKey(item.id);
35157         item.destroy();
35158     },
35159
35160     /**
35161      * Removes and destroys all items in the menu
35162      */
35163     removeAll : function(){
35164         var f;
35165         while(f = this.items.first()){
35166             this.remove(f);
35167         }
35168     }
35169 });
35170
35171 // MenuNav is a private utility class used internally by the Menu
35172 Roo.menu.MenuNav = function(menu){
35173     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35174     this.scope = this.menu = menu;
35175 };
35176
35177 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35178     doRelay : function(e, h){
35179         var k = e.getKey();
35180         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35181             this.menu.tryActivate(0, 1);
35182             return false;
35183         }
35184         return h.call(this.scope || this, e, this.menu);
35185     },
35186
35187     up : function(e, m){
35188         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35189             m.tryActivate(m.items.length-1, -1);
35190         }
35191     },
35192
35193     down : function(e, m){
35194         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35195             m.tryActivate(0, 1);
35196         }
35197     },
35198
35199     right : function(e, m){
35200         if(m.activeItem){
35201             m.activeItem.expandMenu(true);
35202         }
35203     },
35204
35205     left : function(e, m){
35206         m.hide();
35207         if(m.parentMenu && m.parentMenu.activeItem){
35208             m.parentMenu.activeItem.activate();
35209         }
35210     },
35211
35212     enter : function(e, m){
35213         if(m.activeItem){
35214             e.stopPropagation();
35215             m.activeItem.onClick(e);
35216             m.fireEvent("click", this, m.activeItem);
35217             return true;
35218         }
35219     }
35220 });/*
35221  * Based on:
35222  * Ext JS Library 1.1.1
35223  * Copyright(c) 2006-2007, Ext JS, LLC.
35224  *
35225  * Originally Released Under LGPL - original licence link has changed is not relivant.
35226  *
35227  * Fork - LGPL
35228  * <script type="text/javascript">
35229  */
35230  
35231 /**
35232  * @class Roo.menu.MenuMgr
35233  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35234  * @singleton
35235  */
35236 Roo.menu.MenuMgr = function(){
35237    var menus, active, groups = {}, attached = false, lastShow = new Date();
35238
35239    // private - called when first menu is created
35240    function init(){
35241        menus = {};
35242        active = new Roo.util.MixedCollection();
35243        Roo.get(document).addKeyListener(27, function(){
35244            if(active.length > 0){
35245                hideAll();
35246            }
35247        });
35248    }
35249
35250    // private
35251    function hideAll(){
35252        if(active && active.length > 0){
35253            var c = active.clone();
35254            c.each(function(m){
35255                m.hide();
35256            });
35257        }
35258    }
35259
35260    // private
35261    function onHide(m){
35262        active.remove(m);
35263        if(active.length < 1){
35264            Roo.get(document).un("mousedown", onMouseDown);
35265            attached = false;
35266        }
35267    }
35268
35269    // private
35270    function onShow(m){
35271        var last = active.last();
35272        lastShow = new Date();
35273        active.add(m);
35274        if(!attached){
35275            Roo.get(document).on("mousedown", onMouseDown);
35276            attached = true;
35277        }
35278        if(m.parentMenu){
35279           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35280           m.parentMenu.activeChild = m;
35281        }else if(last && last.isVisible()){
35282           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35283        }
35284    }
35285
35286    // private
35287    function onBeforeHide(m){
35288        if(m.activeChild){
35289            m.activeChild.hide();
35290        }
35291        if(m.autoHideTimer){
35292            clearTimeout(m.autoHideTimer);
35293            delete m.autoHideTimer;
35294        }
35295    }
35296
35297    // private
35298    function onBeforeShow(m){
35299        var pm = m.parentMenu;
35300        if(!pm && !m.allowOtherMenus){
35301            hideAll();
35302        }else if(pm && pm.activeChild && active != m){
35303            pm.activeChild.hide();
35304        }
35305    }
35306
35307    // private
35308    function onMouseDown(e){
35309        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35310            hideAll();
35311        }
35312    }
35313
35314    // private
35315    function onBeforeCheck(mi, state){
35316        if(state){
35317            var g = groups[mi.group];
35318            for(var i = 0, l = g.length; i < l; i++){
35319                if(g[i] != mi){
35320                    g[i].setChecked(false);
35321                }
35322            }
35323        }
35324    }
35325
35326    return {
35327
35328        /**
35329         * Hides all menus that are currently visible
35330         */
35331        hideAll : function(){
35332             hideAll();  
35333        },
35334
35335        // private
35336        register : function(menu){
35337            if(!menus){
35338                init();
35339            }
35340            menus[menu.id] = menu;
35341            menu.on("beforehide", onBeforeHide);
35342            menu.on("hide", onHide);
35343            menu.on("beforeshow", onBeforeShow);
35344            menu.on("show", onShow);
35345            var g = menu.group;
35346            if(g && menu.events["checkchange"]){
35347                if(!groups[g]){
35348                    groups[g] = [];
35349                }
35350                groups[g].push(menu);
35351                menu.on("checkchange", onCheck);
35352            }
35353        },
35354
35355         /**
35356          * Returns a {@link Roo.menu.Menu} object
35357          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
35358          * be used to generate and return a new Menu instance.
35359          */
35360        get : function(menu){
35361            if(typeof menu == "string"){ // menu id
35362                return menus[menu];
35363            }else if(menu.events){  // menu instance
35364                return menu;
35365            }else if(typeof menu.length == 'number'){ // array of menu items?
35366                return new Roo.menu.Menu({items:menu});
35367            }else{ // otherwise, must be a config
35368                return new Roo.menu.Menu(menu);
35369            }
35370        },
35371
35372        // private
35373        unregister : function(menu){
35374            delete menus[menu.id];
35375            menu.un("beforehide", onBeforeHide);
35376            menu.un("hide", onHide);
35377            menu.un("beforeshow", onBeforeShow);
35378            menu.un("show", onShow);
35379            var g = menu.group;
35380            if(g && menu.events["checkchange"]){
35381                groups[g].remove(menu);
35382                menu.un("checkchange", onCheck);
35383            }
35384        },
35385
35386        // private
35387        registerCheckable : function(menuItem){
35388            var g = menuItem.group;
35389            if(g){
35390                if(!groups[g]){
35391                    groups[g] = [];
35392                }
35393                groups[g].push(menuItem);
35394                menuItem.on("beforecheckchange", onBeforeCheck);
35395            }
35396        },
35397
35398        // private
35399        unregisterCheckable : function(menuItem){
35400            var g = menuItem.group;
35401            if(g){
35402                groups[g].remove(menuItem);
35403                menuItem.un("beforecheckchange", onBeforeCheck);
35404            }
35405        }
35406    };
35407 }();/*
35408  * Based on:
35409  * Ext JS Library 1.1.1
35410  * Copyright(c) 2006-2007, Ext JS, LLC.
35411  *
35412  * Originally Released Under LGPL - original licence link has changed is not relivant.
35413  *
35414  * Fork - LGPL
35415  * <script type="text/javascript">
35416  */
35417  
35418
35419 /**
35420  * @class Roo.menu.BaseItem
35421  * @extends Roo.Component
35422  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
35423  * management and base configuration options shared by all menu components.
35424  * @constructor
35425  * Creates a new BaseItem
35426  * @param {Object} config Configuration options
35427  */
35428 Roo.menu.BaseItem = function(config){
35429     Roo.menu.BaseItem.superclass.constructor.call(this, config);
35430
35431     this.addEvents({
35432         /**
35433          * @event click
35434          * Fires when this item is clicked
35435          * @param {Roo.menu.BaseItem} this
35436          * @param {Roo.EventObject} e
35437          */
35438         click: true,
35439         /**
35440          * @event activate
35441          * Fires when this item is activated
35442          * @param {Roo.menu.BaseItem} this
35443          */
35444         activate : true,
35445         /**
35446          * @event deactivate
35447          * Fires when this item is deactivated
35448          * @param {Roo.menu.BaseItem} this
35449          */
35450         deactivate : true
35451     });
35452
35453     if(this.handler){
35454         this.on("click", this.handler, this.scope, true);
35455     }
35456 };
35457
35458 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
35459     /**
35460      * @cfg {Function} handler
35461      * A function that will handle the click event of this menu item (defaults to undefined)
35462      */
35463     /**
35464      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
35465      */
35466     canActivate : false,
35467     /**
35468      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
35469      */
35470     activeClass : "x-menu-item-active",
35471     /**
35472      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
35473      */
35474     hideOnClick : true,
35475     /**
35476      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
35477      */
35478     hideDelay : 100,
35479
35480     // private
35481     ctype: "Roo.menu.BaseItem",
35482
35483     // private
35484     actionMode : "container",
35485
35486     // private
35487     render : function(container, parentMenu){
35488         this.parentMenu = parentMenu;
35489         Roo.menu.BaseItem.superclass.render.call(this, container);
35490         this.container.menuItemId = this.id;
35491     },
35492
35493     // private
35494     onRender : function(container, position){
35495         this.el = Roo.get(this.el);
35496         container.dom.appendChild(this.el.dom);
35497     },
35498
35499     // private
35500     onClick : function(e){
35501         if(!this.disabled && this.fireEvent("click", this, e) !== false
35502                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
35503             this.handleClick(e);
35504         }else{
35505             e.stopEvent();
35506         }
35507     },
35508
35509     // private
35510     activate : function(){
35511         if(this.disabled){
35512             return false;
35513         }
35514         var li = this.container;
35515         li.addClass(this.activeClass);
35516         this.region = li.getRegion().adjust(2, 2, -2, -2);
35517         this.fireEvent("activate", this);
35518         return true;
35519     },
35520
35521     // private
35522     deactivate : function(){
35523         this.container.removeClass(this.activeClass);
35524         this.fireEvent("deactivate", this);
35525     },
35526
35527     // private
35528     shouldDeactivate : function(e){
35529         return !this.region || !this.region.contains(e.getPoint());
35530     },
35531
35532     // private
35533     handleClick : function(e){
35534         if(this.hideOnClick){
35535             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
35536         }
35537     },
35538
35539     // private
35540     expandMenu : function(autoActivate){
35541         // do nothing
35542     },
35543
35544     // private
35545     hideMenu : function(){
35546         // do nothing
35547     }
35548 });/*
35549  * Based on:
35550  * Ext JS Library 1.1.1
35551  * Copyright(c) 2006-2007, Ext JS, LLC.
35552  *
35553  * Originally Released Under LGPL - original licence link has changed is not relivant.
35554  *
35555  * Fork - LGPL
35556  * <script type="text/javascript">
35557  */
35558  
35559 /**
35560  * @class Roo.menu.Adapter
35561  * @extends Roo.menu.BaseItem
35562  * 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.
35563  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
35564  * @constructor
35565  * Creates a new Adapter
35566  * @param {Object} config Configuration options
35567  */
35568 Roo.menu.Adapter = function(component, config){
35569     Roo.menu.Adapter.superclass.constructor.call(this, config);
35570     this.component = component;
35571 };
35572 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
35573     // private
35574     canActivate : true,
35575
35576     // private
35577     onRender : function(container, position){
35578         this.component.render(container);
35579         this.el = this.component.getEl();
35580     },
35581
35582     // private
35583     activate : function(){
35584         if(this.disabled){
35585             return false;
35586         }
35587         this.component.focus();
35588         this.fireEvent("activate", this);
35589         return true;
35590     },
35591
35592     // private
35593     deactivate : function(){
35594         this.fireEvent("deactivate", this);
35595     },
35596
35597     // private
35598     disable : function(){
35599         this.component.disable();
35600         Roo.menu.Adapter.superclass.disable.call(this);
35601     },
35602
35603     // private
35604     enable : function(){
35605         this.component.enable();
35606         Roo.menu.Adapter.superclass.enable.call(this);
35607     }
35608 });/*
35609  * Based on:
35610  * Ext JS Library 1.1.1
35611  * Copyright(c) 2006-2007, Ext JS, LLC.
35612  *
35613  * Originally Released Under LGPL - original licence link has changed is not relivant.
35614  *
35615  * Fork - LGPL
35616  * <script type="text/javascript">
35617  */
35618
35619 /**
35620  * @class Roo.menu.TextItem
35621  * @extends Roo.menu.BaseItem
35622  * Adds a static text string to a menu, usually used as either a heading or group separator.
35623  * Note: old style constructor with text is still supported.
35624  * 
35625  * @constructor
35626  * Creates a new TextItem
35627  * @param {Object} cfg Configuration
35628  */
35629 Roo.menu.TextItem = function(cfg){
35630     if (typeof(cfg) == 'string') {
35631         this.text = cfg;
35632     } else {
35633         Roo.apply(this,cfg);
35634     }
35635     
35636     Roo.menu.TextItem.superclass.constructor.call(this);
35637 };
35638
35639 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35640     /**
35641      * @cfg {Boolean} text Text to show on item.
35642      */
35643     text : '',
35644     
35645     /**
35646      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35647      */
35648     hideOnClick : false,
35649     /**
35650      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35651      */
35652     itemCls : "x-menu-text",
35653
35654     // private
35655     onRender : function(){
35656         var s = document.createElement("span");
35657         s.className = this.itemCls;
35658         s.innerHTML = this.text;
35659         this.el = s;
35660         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35661     }
35662 });/*
35663  * Based on:
35664  * Ext JS Library 1.1.1
35665  * Copyright(c) 2006-2007, Ext JS, LLC.
35666  *
35667  * Originally Released Under LGPL - original licence link has changed is not relivant.
35668  *
35669  * Fork - LGPL
35670  * <script type="text/javascript">
35671  */
35672
35673 /**
35674  * @class Roo.menu.Separator
35675  * @extends Roo.menu.BaseItem
35676  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35677  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35678  * @constructor
35679  * @param {Object} config Configuration options
35680  */
35681 Roo.menu.Separator = function(config){
35682     Roo.menu.Separator.superclass.constructor.call(this, config);
35683 };
35684
35685 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35686     /**
35687      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35688      */
35689     itemCls : "x-menu-sep",
35690     /**
35691      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35692      */
35693     hideOnClick : false,
35694
35695     // private
35696     onRender : function(li){
35697         var s = document.createElement("span");
35698         s.className = this.itemCls;
35699         s.innerHTML = "&#160;";
35700         this.el = s;
35701         li.addClass("x-menu-sep-li");
35702         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35703     }
35704 });/*
35705  * Based on:
35706  * Ext JS Library 1.1.1
35707  * Copyright(c) 2006-2007, Ext JS, LLC.
35708  *
35709  * Originally Released Under LGPL - original licence link has changed is not relivant.
35710  *
35711  * Fork - LGPL
35712  * <script type="text/javascript">
35713  */
35714 /**
35715  * @class Roo.menu.Item
35716  * @extends Roo.menu.BaseItem
35717  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35718  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35719  * activation and click handling.
35720  * @constructor
35721  * Creates a new Item
35722  * @param {Object} config Configuration options
35723  */
35724 Roo.menu.Item = function(config){
35725     Roo.menu.Item.superclass.constructor.call(this, config);
35726     if(this.menu){
35727         this.menu = Roo.menu.MenuMgr.get(this.menu);
35728     }
35729 };
35730 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35731     
35732     /**
35733      * @cfg {String} text
35734      * The text to show on the menu item.
35735      */
35736     text: '',
35737      /**
35738      * @cfg {String} HTML to render in menu
35739      * The text to show on the menu item (HTML version).
35740      */
35741     html: '',
35742     /**
35743      * @cfg {String} icon
35744      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35745      */
35746     icon: undefined,
35747     /**
35748      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35749      */
35750     itemCls : "x-menu-item",
35751     /**
35752      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35753      */
35754     canActivate : true,
35755     /**
35756      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35757      */
35758     showDelay: 200,
35759     // doc'd in BaseItem
35760     hideDelay: 200,
35761
35762     // private
35763     ctype: "Roo.menu.Item",
35764     
35765     // private
35766     onRender : function(container, position){
35767         var el = document.createElement("a");
35768         el.hideFocus = true;
35769         el.unselectable = "on";
35770         el.href = this.href || "#";
35771         if(this.hrefTarget){
35772             el.target = this.hrefTarget;
35773         }
35774         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35775         
35776         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35777         
35778         el.innerHTML = String.format(
35779                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35780                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35781         this.el = el;
35782         Roo.menu.Item.superclass.onRender.call(this, container, position);
35783     },
35784
35785     /**
35786      * Sets the text to display in this menu item
35787      * @param {String} text The text to display
35788      * @param {Boolean} isHTML true to indicate text is pure html.
35789      */
35790     setText : function(text, isHTML){
35791         if (isHTML) {
35792             this.html = text;
35793         } else {
35794             this.text = text;
35795             this.html = '';
35796         }
35797         if(this.rendered){
35798             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35799      
35800             this.el.update(String.format(
35801                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35802                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35803             this.parentMenu.autoWidth();
35804         }
35805     },
35806
35807     // private
35808     handleClick : function(e){
35809         if(!this.href){ // if no link defined, stop the event automatically
35810             e.stopEvent();
35811         }
35812         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35813     },
35814
35815     // private
35816     activate : function(autoExpand){
35817         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35818             this.focus();
35819             if(autoExpand){
35820                 this.expandMenu();
35821             }
35822         }
35823         return true;
35824     },
35825
35826     // private
35827     shouldDeactivate : function(e){
35828         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35829             if(this.menu && this.menu.isVisible()){
35830                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35831             }
35832             return true;
35833         }
35834         return false;
35835     },
35836
35837     // private
35838     deactivate : function(){
35839         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35840         this.hideMenu();
35841     },
35842
35843     // private
35844     expandMenu : function(autoActivate){
35845         if(!this.disabled && this.menu){
35846             clearTimeout(this.hideTimer);
35847             delete this.hideTimer;
35848             if(!this.menu.isVisible() && !this.showTimer){
35849                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35850             }else if (this.menu.isVisible() && autoActivate){
35851                 this.menu.tryActivate(0, 1);
35852             }
35853         }
35854     },
35855
35856     // private
35857     deferExpand : function(autoActivate){
35858         delete this.showTimer;
35859         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35860         if(autoActivate){
35861             this.menu.tryActivate(0, 1);
35862         }
35863     },
35864
35865     // private
35866     hideMenu : function(){
35867         clearTimeout(this.showTimer);
35868         delete this.showTimer;
35869         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35870             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35871         }
35872     },
35873
35874     // private
35875     deferHide : function(){
35876         delete this.hideTimer;
35877         this.menu.hide();
35878     }
35879 });/*
35880  * Based on:
35881  * Ext JS Library 1.1.1
35882  * Copyright(c) 2006-2007, Ext JS, LLC.
35883  *
35884  * Originally Released Under LGPL - original licence link has changed is not relivant.
35885  *
35886  * Fork - LGPL
35887  * <script type="text/javascript">
35888  */
35889  
35890 /**
35891  * @class Roo.menu.CheckItem
35892  * @extends Roo.menu.Item
35893  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35894  * @constructor
35895  * Creates a new CheckItem
35896  * @param {Object} config Configuration options
35897  */
35898 Roo.menu.CheckItem = function(config){
35899     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35900     this.addEvents({
35901         /**
35902          * @event beforecheckchange
35903          * Fires before the checked value is set, providing an opportunity to cancel if needed
35904          * @param {Roo.menu.CheckItem} this
35905          * @param {Boolean} checked The new checked value that will be set
35906          */
35907         "beforecheckchange" : true,
35908         /**
35909          * @event checkchange
35910          * Fires after the checked value has been set
35911          * @param {Roo.menu.CheckItem} this
35912          * @param {Boolean} checked The checked value that was set
35913          */
35914         "checkchange" : true
35915     });
35916     if(this.checkHandler){
35917         this.on('checkchange', this.checkHandler, this.scope);
35918     }
35919 };
35920 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35921     /**
35922      * @cfg {String} group
35923      * All check items with the same group name will automatically be grouped into a single-select
35924      * radio button group (defaults to '')
35925      */
35926     /**
35927      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35928      */
35929     itemCls : "x-menu-item x-menu-check-item",
35930     /**
35931      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35932      */
35933     groupClass : "x-menu-group-item",
35934
35935     /**
35936      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35937      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35938      * initialized with checked = true will be rendered as checked.
35939      */
35940     checked: false,
35941
35942     // private
35943     ctype: "Roo.menu.CheckItem",
35944
35945     // private
35946     onRender : function(c){
35947         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35948         if(this.group){
35949             this.el.addClass(this.groupClass);
35950         }
35951         Roo.menu.MenuMgr.registerCheckable(this);
35952         if(this.checked){
35953             this.checked = false;
35954             this.setChecked(true, true);
35955         }
35956     },
35957
35958     // private
35959     destroy : function(){
35960         if(this.rendered){
35961             Roo.menu.MenuMgr.unregisterCheckable(this);
35962         }
35963         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35964     },
35965
35966     /**
35967      * Set the checked state of this item
35968      * @param {Boolean} checked The new checked value
35969      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35970      */
35971     setChecked : function(state, suppressEvent){
35972         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35973             if(this.container){
35974                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35975             }
35976             this.checked = state;
35977             if(suppressEvent !== true){
35978                 this.fireEvent("checkchange", this, state);
35979             }
35980         }
35981     },
35982
35983     // private
35984     handleClick : function(e){
35985        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35986            this.setChecked(!this.checked);
35987        }
35988        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35989     }
35990 });/*
35991  * Based on:
35992  * Ext JS Library 1.1.1
35993  * Copyright(c) 2006-2007, Ext JS, LLC.
35994  *
35995  * Originally Released Under LGPL - original licence link has changed is not relivant.
35996  *
35997  * Fork - LGPL
35998  * <script type="text/javascript">
35999  */
36000  
36001 /**
36002  * @class Roo.menu.DateItem
36003  * @extends Roo.menu.Adapter
36004  * A menu item that wraps the {@link Roo.DatPicker} component.
36005  * @constructor
36006  * Creates a new DateItem
36007  * @param {Object} config Configuration options
36008  */
36009 Roo.menu.DateItem = function(config){
36010     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36011     /** The Roo.DatePicker object @type Roo.DatePicker */
36012     this.picker = this.component;
36013     this.addEvents({select: true});
36014     
36015     this.picker.on("render", function(picker){
36016         picker.getEl().swallowEvent("click");
36017         picker.container.addClass("x-menu-date-item");
36018     });
36019
36020     this.picker.on("select", this.onSelect, this);
36021 };
36022
36023 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36024     // private
36025     onSelect : function(picker, date){
36026         this.fireEvent("select", this, date, picker);
36027         Roo.menu.DateItem.superclass.handleClick.call(this);
36028     }
36029 });/*
36030  * Based on:
36031  * Ext JS Library 1.1.1
36032  * Copyright(c) 2006-2007, Ext JS, LLC.
36033  *
36034  * Originally Released Under LGPL - original licence link has changed is not relivant.
36035  *
36036  * Fork - LGPL
36037  * <script type="text/javascript">
36038  */
36039  
36040 /**
36041  * @class Roo.menu.ColorItem
36042  * @extends Roo.menu.Adapter
36043  * A menu item that wraps the {@link Roo.ColorPalette} component.
36044  * @constructor
36045  * Creates a new ColorItem
36046  * @param {Object} config Configuration options
36047  */
36048 Roo.menu.ColorItem = function(config){
36049     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36050     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36051     this.palette = this.component;
36052     this.relayEvents(this.palette, ["select"]);
36053     if(this.selectHandler){
36054         this.on('select', this.selectHandler, this.scope);
36055     }
36056 };
36057 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36058  * Based on:
36059  * Ext JS Library 1.1.1
36060  * Copyright(c) 2006-2007, Ext JS, LLC.
36061  *
36062  * Originally Released Under LGPL - original licence link has changed is not relivant.
36063  *
36064  * Fork - LGPL
36065  * <script type="text/javascript">
36066  */
36067  
36068
36069 /**
36070  * @class Roo.menu.DateMenu
36071  * @extends Roo.menu.Menu
36072  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36073  * @constructor
36074  * Creates a new DateMenu
36075  * @param {Object} config Configuration options
36076  */
36077 Roo.menu.DateMenu = function(config){
36078     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36079     this.plain = true;
36080     var di = new Roo.menu.DateItem(config);
36081     this.add(di);
36082     /**
36083      * The {@link Roo.DatePicker} instance for this DateMenu
36084      * @type DatePicker
36085      */
36086     this.picker = di.picker;
36087     /**
36088      * @event select
36089      * @param {DatePicker} picker
36090      * @param {Date} date
36091      */
36092     this.relayEvents(di, ["select"]);
36093     this.on('beforeshow', function(){
36094         if(this.picker){
36095             this.picker.hideMonthPicker(false);
36096         }
36097     }, this);
36098 };
36099 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36100     cls:'x-date-menu'
36101 });/*
36102  * Based on:
36103  * Ext JS Library 1.1.1
36104  * Copyright(c) 2006-2007, Ext JS, LLC.
36105  *
36106  * Originally Released Under LGPL - original licence link has changed is not relivant.
36107  *
36108  * Fork - LGPL
36109  * <script type="text/javascript">
36110  */
36111  
36112
36113 /**
36114  * @class Roo.menu.ColorMenu
36115  * @extends Roo.menu.Menu
36116  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36117  * @constructor
36118  * Creates a new ColorMenu
36119  * @param {Object} config Configuration options
36120  */
36121 Roo.menu.ColorMenu = function(config){
36122     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36123     this.plain = true;
36124     var ci = new Roo.menu.ColorItem(config);
36125     this.add(ci);
36126     /**
36127      * The {@link Roo.ColorPalette} instance for this ColorMenu
36128      * @type ColorPalette
36129      */
36130     this.palette = ci.palette;
36131     /**
36132      * @event select
36133      * @param {ColorPalette} palette
36134      * @param {String} color
36135      */
36136     this.relayEvents(ci, ["select"]);
36137 };
36138 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36139  * Based on:
36140  * Ext JS Library 1.1.1
36141  * Copyright(c) 2006-2007, Ext JS, LLC.
36142  *
36143  * Originally Released Under LGPL - original licence link has changed is not relivant.
36144  *
36145  * Fork - LGPL
36146  * <script type="text/javascript">
36147  */
36148  
36149 /**
36150  * @class Roo.form.Field
36151  * @extends Roo.BoxComponent
36152  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36153  * @constructor
36154  * Creates a new Field
36155  * @param {Object} config Configuration options
36156  */
36157 Roo.form.Field = function(config){
36158     Roo.form.Field.superclass.constructor.call(this, config);
36159 };
36160
36161 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36162     /**
36163      * @cfg {String} fieldLabel Label to use when rendering a form.
36164      */
36165        /**
36166      * @cfg {String} qtip Mouse over tip
36167      */
36168      
36169     /**
36170      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36171      */
36172     invalidClass : "x-form-invalid",
36173     /**
36174      * @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")
36175      */
36176     invalidText : "The value in this field is invalid",
36177     /**
36178      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36179      */
36180     focusClass : "x-form-focus",
36181     /**
36182      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36183       automatic validation (defaults to "keyup").
36184      */
36185     validationEvent : "keyup",
36186     /**
36187      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36188      */
36189     validateOnBlur : true,
36190     /**
36191      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36192      */
36193     validationDelay : 250,
36194     /**
36195      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36196      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36197      */
36198     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36199     /**
36200      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36201      */
36202     fieldClass : "x-form-field",
36203     /**
36204      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36205      *<pre>
36206 Value         Description
36207 -----------   ----------------------------------------------------------------------
36208 qtip          Display a quick tip when the user hovers over the field
36209 title         Display a default browser title attribute popup
36210 under         Add a block div beneath the field containing the error text
36211 side          Add an error icon to the right of the field with a popup on hover
36212 [element id]  Add the error text directly to the innerHTML of the specified element
36213 </pre>
36214      */
36215     msgTarget : 'qtip',
36216     /**
36217      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36218      */
36219     msgFx : 'normal',
36220
36221     /**
36222      * @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.
36223      */
36224     readOnly : false,
36225
36226     /**
36227      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36228      */
36229     disabled : false,
36230
36231     /**
36232      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36233      */
36234     inputType : undefined,
36235     
36236     /**
36237      * @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).
36238          */
36239         tabIndex : undefined,
36240         
36241     // private
36242     isFormField : true,
36243
36244     // private
36245     hasFocus : false,
36246     /**
36247      * @property {Roo.Element} fieldEl
36248      * Element Containing the rendered Field (with label etc.)
36249      */
36250     /**
36251      * @cfg {Mixed} value A value to initialize this field with.
36252      */
36253     value : undefined,
36254
36255     /**
36256      * @cfg {String} name The field's HTML name attribute.
36257      */
36258     /**
36259      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36260      */
36261
36262         // private ??
36263         initComponent : function(){
36264         Roo.form.Field.superclass.initComponent.call(this);
36265         this.addEvents({
36266             /**
36267              * @event focus
36268              * Fires when this field receives input focus.
36269              * @param {Roo.form.Field} this
36270              */
36271             focus : true,
36272             /**
36273              * @event blur
36274              * Fires when this field loses input focus.
36275              * @param {Roo.form.Field} this
36276              */
36277             blur : true,
36278             /**
36279              * @event specialkey
36280              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36281              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36282              * @param {Roo.form.Field} this
36283              * @param {Roo.EventObject} e The event object
36284              */
36285             specialkey : true,
36286             /**
36287              * @event change
36288              * Fires just before the field blurs if the field value has changed.
36289              * @param {Roo.form.Field} this
36290              * @param {Mixed} newValue The new value
36291              * @param {Mixed} oldValue The original value
36292              */
36293             change : true,
36294             /**
36295              * @event invalid
36296              * Fires after the field has been marked as invalid.
36297              * @param {Roo.form.Field} this
36298              * @param {String} msg The validation message
36299              */
36300             invalid : true,
36301             /**
36302              * @event valid
36303              * Fires after the field has been validated with no errors.
36304              * @param {Roo.form.Field} this
36305              */
36306             valid : true,
36307              /**
36308              * @event keyup
36309              * Fires after the key up
36310              * @param {Roo.form.Field} this
36311              * @param {Roo.EventObject}  e The event Object
36312              */
36313             keyup : true
36314         });
36315     },
36316
36317     /**
36318      * Returns the name attribute of the field if available
36319      * @return {String} name The field name
36320      */
36321     getName: function(){
36322          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36323     },
36324
36325     // private
36326     onRender : function(ct, position){
36327         Roo.form.Field.superclass.onRender.call(this, ct, position);
36328         if(!this.el){
36329             var cfg = this.getAutoCreate();
36330             if(!cfg.name){
36331                 cfg.name = this.name || this.id;
36332             }
36333             if(this.inputType){
36334                 cfg.type = this.inputType;
36335             }
36336             this.el = ct.createChild(cfg, position);
36337         }
36338         var type = this.el.dom.type;
36339         if(type){
36340             if(type == 'password'){
36341                 type = 'text';
36342             }
36343             this.el.addClass('x-form-'+type);
36344         }
36345         if(this.readOnly){
36346             this.el.dom.readOnly = true;
36347         }
36348         if(this.tabIndex !== undefined){
36349             this.el.dom.setAttribute('tabIndex', this.tabIndex);
36350         }
36351
36352         this.el.addClass([this.fieldClass, this.cls]);
36353         this.initValue();
36354     },
36355
36356     /**
36357      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
36358      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
36359      * @return {Roo.form.Field} this
36360      */
36361     applyTo : function(target){
36362         this.allowDomMove = false;
36363         this.el = Roo.get(target);
36364         this.render(this.el.dom.parentNode);
36365         return this;
36366     },
36367
36368     // private
36369     initValue : function(){
36370         if(this.value !== undefined){
36371             this.setValue(this.value);
36372         }else if(this.el.dom.value.length > 0){
36373             this.setValue(this.el.dom.value);
36374         }
36375     },
36376
36377     /**
36378      * Returns true if this field has been changed since it was originally loaded and is not disabled.
36379      */
36380     isDirty : function() {
36381         if(this.disabled) {
36382             return false;
36383         }
36384         return String(this.getValue()) !== String(this.originalValue);
36385     },
36386
36387     // private
36388     afterRender : function(){
36389         Roo.form.Field.superclass.afterRender.call(this);
36390         this.initEvents();
36391     },
36392
36393     // private
36394     fireKey : function(e){
36395         //Roo.log('field ' + e.getKey());
36396         if(e.isNavKeyPress()){
36397             this.fireEvent("specialkey", this, e);
36398         }
36399     },
36400
36401     /**
36402      * Resets the current field value to the originally loaded value and clears any validation messages
36403      */
36404     reset : function(){
36405         this.setValue(this.originalValue);
36406         this.clearInvalid();
36407     },
36408
36409     // private
36410     initEvents : function(){
36411         // safari killled keypress - so keydown is now used..
36412         this.el.on("keydown" , this.fireKey,  this);
36413         this.el.on("focus", this.onFocus,  this);
36414         this.el.on("blur", this.onBlur,  this);
36415         this.el.relayEvent('keyup', this);
36416
36417         // reference to original value for reset
36418         this.originalValue = this.getValue();
36419     },
36420
36421     // private
36422     onFocus : function(){
36423         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36424             this.el.addClass(this.focusClass);
36425         }
36426         if(!this.hasFocus){
36427             this.hasFocus = true;
36428             this.startValue = this.getValue();
36429             this.fireEvent("focus", this);
36430         }
36431     },
36432
36433     beforeBlur : Roo.emptyFn,
36434
36435     // private
36436     onBlur : function(){
36437         this.beforeBlur();
36438         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36439             this.el.removeClass(this.focusClass);
36440         }
36441         this.hasFocus = false;
36442         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
36443             this.validate();
36444         }
36445         var v = this.getValue();
36446         if(String(v) !== String(this.startValue)){
36447             this.fireEvent('change', this, v, this.startValue);
36448         }
36449         this.fireEvent("blur", this);
36450     },
36451
36452     /**
36453      * Returns whether or not the field value is currently valid
36454      * @param {Boolean} preventMark True to disable marking the field invalid
36455      * @return {Boolean} True if the value is valid, else false
36456      */
36457     isValid : function(preventMark){
36458         if(this.disabled){
36459             return true;
36460         }
36461         var restore = this.preventMark;
36462         this.preventMark = preventMark === true;
36463         var v = this.validateValue(this.processValue(this.getRawValue()));
36464         this.preventMark = restore;
36465         return v;
36466     },
36467
36468     /**
36469      * Validates the field value
36470      * @return {Boolean} True if the value is valid, else false
36471      */
36472     validate : function(){
36473         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
36474             this.clearInvalid();
36475             return true;
36476         }
36477         return false;
36478     },
36479
36480     processValue : function(value){
36481         return value;
36482     },
36483
36484     // private
36485     // Subclasses should provide the validation implementation by overriding this
36486     validateValue : function(value){
36487         return true;
36488     },
36489
36490     /**
36491      * Mark this field as invalid
36492      * @param {String} msg The validation message
36493      */
36494     markInvalid : function(msg){
36495         if(!this.rendered || this.preventMark){ // not rendered
36496             return;
36497         }
36498         this.el.addClass(this.invalidClass);
36499         msg = msg || this.invalidText;
36500         switch(this.msgTarget){
36501             case 'qtip':
36502                 this.el.dom.qtip = msg;
36503                 this.el.dom.qclass = 'x-form-invalid-tip';
36504                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
36505                     Roo.QuickTips.enable();
36506                 }
36507                 break;
36508             case 'title':
36509                 this.el.dom.title = msg;
36510                 break;
36511             case 'under':
36512                 if(!this.errorEl){
36513                     var elp = this.el.findParent('.x-form-element', 5, true);
36514                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
36515                     this.errorEl.setWidth(elp.getWidth(true)-20);
36516                 }
36517                 this.errorEl.update(msg);
36518                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
36519                 break;
36520             case 'side':
36521                 if(!this.errorIcon){
36522                     var elp = this.el.findParent('.x-form-element', 5, true);
36523                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
36524                 }
36525                 this.alignErrorIcon();
36526                 this.errorIcon.dom.qtip = msg;
36527                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
36528                 this.errorIcon.show();
36529                 this.on('resize', this.alignErrorIcon, this);
36530                 break;
36531             default:
36532                 var t = Roo.getDom(this.msgTarget);
36533                 t.innerHTML = msg;
36534                 t.style.display = this.msgDisplay;
36535                 break;
36536         }
36537         this.fireEvent('invalid', this, msg);
36538     },
36539
36540     // private
36541     alignErrorIcon : function(){
36542         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
36543     },
36544
36545     /**
36546      * Clear any invalid styles/messages for this field
36547      */
36548     clearInvalid : function(){
36549         if(!this.rendered || this.preventMark){ // not rendered
36550             return;
36551         }
36552         this.el.removeClass(this.invalidClass);
36553         switch(this.msgTarget){
36554             case 'qtip':
36555                 this.el.dom.qtip = '';
36556                 break;
36557             case 'title':
36558                 this.el.dom.title = '';
36559                 break;
36560             case 'under':
36561                 if(this.errorEl){
36562                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
36563                 }
36564                 break;
36565             case 'side':
36566                 if(this.errorIcon){
36567                     this.errorIcon.dom.qtip = '';
36568                     this.errorIcon.hide();
36569                     this.un('resize', this.alignErrorIcon, this);
36570                 }
36571                 break;
36572             default:
36573                 var t = Roo.getDom(this.msgTarget);
36574                 t.innerHTML = '';
36575                 t.style.display = 'none';
36576                 break;
36577         }
36578         this.fireEvent('valid', this);
36579     },
36580
36581     /**
36582      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
36583      * @return {Mixed} value The field value
36584      */
36585     getRawValue : function(){
36586         var v = this.el.getValue();
36587         if(v === this.emptyText){
36588             v = '';
36589         }
36590         return v;
36591     },
36592
36593     /**
36594      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
36595      * @return {Mixed} value The field value
36596      */
36597     getValue : function(){
36598         var v = this.el.getValue();
36599         if(v === this.emptyText || v === undefined){
36600             v = '';
36601         }
36602         return v;
36603     },
36604
36605     /**
36606      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
36607      * @param {Mixed} value The value to set
36608      */
36609     setRawValue : function(v){
36610         return this.el.dom.value = (v === null || v === undefined ? '' : v);
36611     },
36612
36613     /**
36614      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
36615      * @param {Mixed} value The value to set
36616      */
36617     setValue : function(v){
36618         this.value = v;
36619         if(this.rendered){
36620             this.el.dom.value = (v === null || v === undefined ? '' : v);
36621              this.validate();
36622         }
36623     },
36624
36625     adjustSize : function(w, h){
36626         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
36627         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
36628         return s;
36629     },
36630
36631     adjustWidth : function(tag, w){
36632         tag = tag.toLowerCase();
36633         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36634             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36635                 if(tag == 'input'){
36636                     return w + 2;
36637                 }
36638                 if(tag = 'textarea'){
36639                     return w-2;
36640                 }
36641             }else if(Roo.isOpera){
36642                 if(tag == 'input'){
36643                     return w + 2;
36644                 }
36645                 if(tag = 'textarea'){
36646                     return w-2;
36647                 }
36648             }
36649         }
36650         return w;
36651     }
36652 });
36653
36654
36655 // anything other than normal should be considered experimental
36656 Roo.form.Field.msgFx = {
36657     normal : {
36658         show: function(msgEl, f){
36659             msgEl.setDisplayed('block');
36660         },
36661
36662         hide : function(msgEl, f){
36663             msgEl.setDisplayed(false).update('');
36664         }
36665     },
36666
36667     slide : {
36668         show: function(msgEl, f){
36669             msgEl.slideIn('t', {stopFx:true});
36670         },
36671
36672         hide : function(msgEl, f){
36673             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36674         }
36675     },
36676
36677     slideRight : {
36678         show: function(msgEl, f){
36679             msgEl.fixDisplay();
36680             msgEl.alignTo(f.el, 'tl-tr');
36681             msgEl.slideIn('l', {stopFx:true});
36682         },
36683
36684         hide : function(msgEl, f){
36685             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36686         }
36687     }
36688 };/*
36689  * Based on:
36690  * Ext JS Library 1.1.1
36691  * Copyright(c) 2006-2007, Ext JS, LLC.
36692  *
36693  * Originally Released Under LGPL - original licence link has changed is not relivant.
36694  *
36695  * Fork - LGPL
36696  * <script type="text/javascript">
36697  */
36698  
36699
36700 /**
36701  * @class Roo.form.TextField
36702  * @extends Roo.form.Field
36703  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36704  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36705  * @constructor
36706  * Creates a new TextField
36707  * @param {Object} config Configuration options
36708  */
36709 Roo.form.TextField = function(config){
36710     Roo.form.TextField.superclass.constructor.call(this, config);
36711     this.addEvents({
36712         /**
36713          * @event autosize
36714          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36715          * according to the default logic, but this event provides a hook for the developer to apply additional
36716          * logic at runtime to resize the field if needed.
36717              * @param {Roo.form.Field} this This text field
36718              * @param {Number} width The new field width
36719              */
36720         autosize : true
36721     });
36722 };
36723
36724 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36725     /**
36726      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36727      */
36728     grow : false,
36729     /**
36730      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36731      */
36732     growMin : 30,
36733     /**
36734      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36735      */
36736     growMax : 800,
36737     /**
36738      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36739      */
36740     vtype : null,
36741     /**
36742      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36743      */
36744     maskRe : null,
36745     /**
36746      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36747      */
36748     disableKeyFilter : false,
36749     /**
36750      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36751      */
36752     allowBlank : true,
36753     /**
36754      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36755      */
36756     minLength : 0,
36757     /**
36758      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36759      */
36760     maxLength : Number.MAX_VALUE,
36761     /**
36762      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36763      */
36764     minLengthText : "The minimum length for this field is {0}",
36765     /**
36766      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36767      */
36768     maxLengthText : "The maximum length for this field is {0}",
36769     /**
36770      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36771      */
36772     selectOnFocus : false,
36773     /**
36774      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36775      */
36776     blankText : "This field is required",
36777     /**
36778      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36779      * If available, this function will be called only after the basic validators all return true, and will be passed the
36780      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36781      */
36782     validator : null,
36783     /**
36784      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36785      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36786      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36787      */
36788     regex : null,
36789     /**
36790      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36791      */
36792     regexText : "",
36793     /**
36794      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36795      */
36796     emptyText : null,
36797     /**
36798      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36799      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36800      */
36801     emptyClass : 'x-form-empty-field',
36802
36803     // private
36804     initEvents : function(){
36805         Roo.form.TextField.superclass.initEvents.call(this);
36806         if(this.validationEvent == 'keyup'){
36807             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36808             this.el.on('keyup', this.filterValidation, this);
36809         }
36810         else if(this.validationEvent !== false){
36811             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36812         }
36813         if(this.selectOnFocus || this.emptyText){
36814             this.on("focus", this.preFocus, this);
36815             if(this.emptyText){
36816                 this.on('blur', this.postBlur, this);
36817                 this.applyEmptyText();
36818             }
36819         }
36820         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36821             this.el.on("keypress", this.filterKeys, this);
36822         }
36823         if(this.grow){
36824             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36825             this.el.on("click", this.autoSize,  this);
36826         }
36827     },
36828
36829     processValue : function(value){
36830         if(this.stripCharsRe){
36831             var newValue = value.replace(this.stripCharsRe, '');
36832             if(newValue !== value){
36833                 this.setRawValue(newValue);
36834                 return newValue;
36835             }
36836         }
36837         return value;
36838     },
36839
36840     filterValidation : function(e){
36841         if(!e.isNavKeyPress()){
36842             this.validationTask.delay(this.validationDelay);
36843         }
36844     },
36845
36846     // private
36847     onKeyUp : function(e){
36848         if(!e.isNavKeyPress()){
36849             this.autoSize();
36850         }
36851     },
36852
36853     /**
36854      * Resets the current field value to the originally-loaded value and clears any validation messages.
36855      * Also adds emptyText and emptyClass if the original value was blank.
36856      */
36857     reset : function(){
36858         Roo.form.TextField.superclass.reset.call(this);
36859         this.applyEmptyText();
36860     },
36861
36862     applyEmptyText : function(){
36863         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36864             this.setRawValue(this.emptyText);
36865             this.el.addClass(this.emptyClass);
36866         }
36867     },
36868
36869     // private
36870     preFocus : function(){
36871         if(this.emptyText){
36872             if(this.el.dom.value == this.emptyText){
36873                 this.setRawValue('');
36874             }
36875             this.el.removeClass(this.emptyClass);
36876         }
36877         if(this.selectOnFocus){
36878             this.el.dom.select();
36879         }
36880     },
36881
36882     // private
36883     postBlur : function(){
36884         this.applyEmptyText();
36885     },
36886
36887     // private
36888     filterKeys : function(e){
36889         var k = e.getKey();
36890         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36891             return;
36892         }
36893         var c = e.getCharCode(), cc = String.fromCharCode(c);
36894         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36895             return;
36896         }
36897         if(!this.maskRe.test(cc)){
36898             e.stopEvent();
36899         }
36900     },
36901
36902     setValue : function(v){
36903         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36904             this.el.removeClass(this.emptyClass);
36905         }
36906         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36907         this.applyEmptyText();
36908         this.autoSize();
36909     },
36910
36911     /**
36912      * Validates a value according to the field's validation rules and marks the field as invalid
36913      * if the validation fails
36914      * @param {Mixed} value The value to validate
36915      * @return {Boolean} True if the value is valid, else false
36916      */
36917     validateValue : function(value){
36918         if(value.length < 1 || value === this.emptyText){ // if it's blank
36919              if(this.allowBlank){
36920                 this.clearInvalid();
36921                 return true;
36922              }else{
36923                 this.markInvalid(this.blankText);
36924                 return false;
36925              }
36926         }
36927         if(value.length < this.minLength){
36928             this.markInvalid(String.format(this.minLengthText, this.minLength));
36929             return false;
36930         }
36931         if(value.length > this.maxLength){
36932             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36933             return false;
36934         }
36935         if(this.vtype){
36936             var vt = Roo.form.VTypes;
36937             if(!vt[this.vtype](value, this)){
36938                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36939                 return false;
36940             }
36941         }
36942         if(typeof this.validator == "function"){
36943             var msg = this.validator(value);
36944             if(msg !== true){
36945                 this.markInvalid(msg);
36946                 return false;
36947             }
36948         }
36949         if(this.regex && !this.regex.test(value)){
36950             this.markInvalid(this.regexText);
36951             return false;
36952         }
36953         return true;
36954     },
36955
36956     /**
36957      * Selects text in this field
36958      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36959      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36960      */
36961     selectText : function(start, end){
36962         var v = this.getRawValue();
36963         if(v.length > 0){
36964             start = start === undefined ? 0 : start;
36965             end = end === undefined ? v.length : end;
36966             var d = this.el.dom;
36967             if(d.setSelectionRange){
36968                 d.setSelectionRange(start, end);
36969             }else if(d.createTextRange){
36970                 var range = d.createTextRange();
36971                 range.moveStart("character", start);
36972                 range.moveEnd("character", v.length-end);
36973                 range.select();
36974             }
36975         }
36976     },
36977
36978     /**
36979      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36980      * This only takes effect if grow = true, and fires the autosize event.
36981      */
36982     autoSize : function(){
36983         if(!this.grow || !this.rendered){
36984             return;
36985         }
36986         if(!this.metrics){
36987             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36988         }
36989         var el = this.el;
36990         var v = el.dom.value;
36991         var d = document.createElement('div');
36992         d.appendChild(document.createTextNode(v));
36993         v = d.innerHTML;
36994         d = null;
36995         v += "&#160;";
36996         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36997         this.el.setWidth(w);
36998         this.fireEvent("autosize", this, w);
36999     }
37000 });/*
37001  * Based on:
37002  * Ext JS Library 1.1.1
37003  * Copyright(c) 2006-2007, Ext JS, LLC.
37004  *
37005  * Originally Released Under LGPL - original licence link has changed is not relivant.
37006  *
37007  * Fork - LGPL
37008  * <script type="text/javascript">
37009  */
37010  
37011 /**
37012  * @class Roo.form.Hidden
37013  * @extends Roo.form.TextField
37014  * Simple Hidden element used on forms 
37015  * 
37016  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37017  * 
37018  * @constructor
37019  * Creates a new Hidden form element.
37020  * @param {Object} config Configuration options
37021  */
37022
37023
37024
37025 // easy hidden field...
37026 Roo.form.Hidden = function(config){
37027     Roo.form.Hidden.superclass.constructor.call(this, config);
37028 };
37029   
37030 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37031     fieldLabel:      '',
37032     inputType:      'hidden',
37033     width:          50,
37034     allowBlank:     true,
37035     labelSeparator: '',
37036     hidden:         true,
37037     itemCls :       'x-form-item-display-none'
37038
37039
37040 });
37041
37042
37043 /*
37044  * Based on:
37045  * Ext JS Library 1.1.1
37046  * Copyright(c) 2006-2007, Ext JS, LLC.
37047  *
37048  * Originally Released Under LGPL - original licence link has changed is not relivant.
37049  *
37050  * Fork - LGPL
37051  * <script type="text/javascript">
37052  */
37053  
37054 /**
37055  * @class Roo.form.TriggerField
37056  * @extends Roo.form.TextField
37057  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37058  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37059  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37060  * for which you can provide a custom implementation.  For example:
37061  * <pre><code>
37062 var trigger = new Roo.form.TriggerField();
37063 trigger.onTriggerClick = myTriggerFn;
37064 trigger.applyTo('my-field');
37065 </code></pre>
37066  *
37067  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37068  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37069  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37070  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37071  * @constructor
37072  * Create a new TriggerField.
37073  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37074  * to the base TextField)
37075  */
37076 Roo.form.TriggerField = function(config){
37077     this.mimicing = false;
37078     Roo.form.TriggerField.superclass.constructor.call(this, config);
37079 };
37080
37081 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37082     /**
37083      * @cfg {String} triggerClass A CSS class to apply to the trigger
37084      */
37085     /**
37086      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37087      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37088      */
37089     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37090     /**
37091      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37092      */
37093     hideTrigger:false,
37094
37095     /** @cfg {Boolean} grow @hide */
37096     /** @cfg {Number} growMin @hide */
37097     /** @cfg {Number} growMax @hide */
37098
37099     /**
37100      * @hide 
37101      * @method
37102      */
37103     autoSize: Roo.emptyFn,
37104     // private
37105     monitorTab : true,
37106     // private
37107     deferHeight : true,
37108
37109     
37110     actionMode : 'wrap',
37111     // private
37112     onResize : function(w, h){
37113         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37114         if(typeof w == 'number'){
37115             var x = w - this.trigger.getWidth();
37116             this.el.setWidth(this.adjustWidth('input', x));
37117             this.trigger.setStyle('left', x+'px');
37118         }
37119     },
37120
37121     // private
37122     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37123
37124     // private
37125     getResizeEl : function(){
37126         return this.wrap;
37127     },
37128
37129     // private
37130     getPositionEl : function(){
37131         return this.wrap;
37132     },
37133
37134     // private
37135     alignErrorIcon : function(){
37136         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37137     },
37138
37139     // private
37140     onRender : function(ct, position){
37141         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37142         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37143         this.trigger = this.wrap.createChild(this.triggerConfig ||
37144                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37145         if(this.hideTrigger){
37146             this.trigger.setDisplayed(false);
37147         }
37148         this.initTrigger();
37149         if(!this.width){
37150             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37151         }
37152     },
37153
37154     // private
37155     initTrigger : function(){
37156         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37157         this.trigger.addClassOnOver('x-form-trigger-over');
37158         this.trigger.addClassOnClick('x-form-trigger-click');
37159     },
37160
37161     // private
37162     onDestroy : function(){
37163         if(this.trigger){
37164             this.trigger.removeAllListeners();
37165             this.trigger.remove();
37166         }
37167         if(this.wrap){
37168             this.wrap.remove();
37169         }
37170         Roo.form.TriggerField.superclass.onDestroy.call(this);
37171     },
37172
37173     // private
37174     onFocus : function(){
37175         Roo.form.TriggerField.superclass.onFocus.call(this);
37176         if(!this.mimicing){
37177             this.wrap.addClass('x-trigger-wrap-focus');
37178             this.mimicing = true;
37179             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37180             if(this.monitorTab){
37181                 this.el.on("keydown", this.checkTab, this);
37182             }
37183         }
37184     },
37185
37186     // private
37187     checkTab : function(e){
37188         if(e.getKey() == e.TAB){
37189             this.triggerBlur();
37190         }
37191     },
37192
37193     // private
37194     onBlur : function(){
37195         // do nothing
37196     },
37197
37198     // private
37199     mimicBlur : function(e, t){
37200         if(!this.wrap.contains(t) && this.validateBlur()){
37201             this.triggerBlur();
37202         }
37203     },
37204
37205     // private
37206     triggerBlur : function(){
37207         this.mimicing = false;
37208         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37209         if(this.monitorTab){
37210             this.el.un("keydown", this.checkTab, this);
37211         }
37212         this.wrap.removeClass('x-trigger-wrap-focus');
37213         Roo.form.TriggerField.superclass.onBlur.call(this);
37214     },
37215
37216     // private
37217     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37218     validateBlur : function(e, t){
37219         return true;
37220     },
37221
37222     // private
37223     onDisable : function(){
37224         Roo.form.TriggerField.superclass.onDisable.call(this);
37225         if(this.wrap){
37226             this.wrap.addClass('x-item-disabled');
37227         }
37228     },
37229
37230     // private
37231     onEnable : function(){
37232         Roo.form.TriggerField.superclass.onEnable.call(this);
37233         if(this.wrap){
37234             this.wrap.removeClass('x-item-disabled');
37235         }
37236     },
37237
37238     // private
37239     onShow : function(){
37240         var ae = this.getActionEl();
37241         
37242         if(ae){
37243             ae.dom.style.display = '';
37244             ae.dom.style.visibility = 'visible';
37245         }
37246     },
37247
37248     // private
37249     
37250     onHide : function(){
37251         var ae = this.getActionEl();
37252         ae.dom.style.display = 'none';
37253     },
37254
37255     /**
37256      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37257      * by an implementing function.
37258      * @method
37259      * @param {EventObject} e
37260      */
37261     onTriggerClick : Roo.emptyFn
37262 });
37263
37264 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37265 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37266 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37267 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37268     initComponent : function(){
37269         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37270
37271         this.triggerConfig = {
37272             tag:'span', cls:'x-form-twin-triggers', cn:[
37273             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37274             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37275         ]};
37276     },
37277
37278     getTrigger : function(index){
37279         return this.triggers[index];
37280     },
37281
37282     initTrigger : function(){
37283         var ts = this.trigger.select('.x-form-trigger', true);
37284         this.wrap.setStyle('overflow', 'hidden');
37285         var triggerField = this;
37286         ts.each(function(t, all, index){
37287             t.hide = function(){
37288                 var w = triggerField.wrap.getWidth();
37289                 this.dom.style.display = 'none';
37290                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37291             };
37292             t.show = function(){
37293                 var w = triggerField.wrap.getWidth();
37294                 this.dom.style.display = '';
37295                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37296             };
37297             var triggerIndex = 'Trigger'+(index+1);
37298
37299             if(this['hide'+triggerIndex]){
37300                 t.dom.style.display = 'none';
37301             }
37302             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37303             t.addClassOnOver('x-form-trigger-over');
37304             t.addClassOnClick('x-form-trigger-click');
37305         }, this);
37306         this.triggers = ts.elements;
37307     },
37308
37309     onTrigger1Click : Roo.emptyFn,
37310     onTrigger2Click : Roo.emptyFn
37311 });/*
37312  * Based on:
37313  * Ext JS Library 1.1.1
37314  * Copyright(c) 2006-2007, Ext JS, LLC.
37315  *
37316  * Originally Released Under LGPL - original licence link has changed is not relivant.
37317  *
37318  * Fork - LGPL
37319  * <script type="text/javascript">
37320  */
37321  
37322 /**
37323  * @class Roo.form.TextArea
37324  * @extends Roo.form.TextField
37325  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37326  * support for auto-sizing.
37327  * @constructor
37328  * Creates a new TextArea
37329  * @param {Object} config Configuration options
37330  */
37331 Roo.form.TextArea = function(config){
37332     Roo.form.TextArea.superclass.constructor.call(this, config);
37333     // these are provided exchanges for backwards compat
37334     // minHeight/maxHeight were replaced by growMin/growMax to be
37335     // compatible with TextField growing config values
37336     if(this.minHeight !== undefined){
37337         this.growMin = this.minHeight;
37338     }
37339     if(this.maxHeight !== undefined){
37340         this.growMax = this.maxHeight;
37341     }
37342 };
37343
37344 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
37345     /**
37346      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
37347      */
37348     growMin : 60,
37349     /**
37350      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
37351      */
37352     growMax: 1000,
37353     /**
37354      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
37355      * in the field (equivalent to setting overflow: hidden, defaults to false)
37356      */
37357     preventScrollbars: false,
37358     /**
37359      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37360      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
37361      */
37362
37363     // private
37364     onRender : function(ct, position){
37365         if(!this.el){
37366             this.defaultAutoCreate = {
37367                 tag: "textarea",
37368                 style:"width:300px;height:60px;",
37369                 autocomplete: "off"
37370             };
37371         }
37372         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
37373         if(this.grow){
37374             this.textSizeEl = Roo.DomHelper.append(document.body, {
37375                 tag: "pre", cls: "x-form-grow-sizer"
37376             });
37377             if(this.preventScrollbars){
37378                 this.el.setStyle("overflow", "hidden");
37379             }
37380             this.el.setHeight(this.growMin);
37381         }
37382     },
37383
37384     onDestroy : function(){
37385         if(this.textSizeEl){
37386             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
37387         }
37388         Roo.form.TextArea.superclass.onDestroy.call(this);
37389     },
37390
37391     // private
37392     onKeyUp : function(e){
37393         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
37394             this.autoSize();
37395         }
37396     },
37397
37398     /**
37399      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
37400      * This only takes effect if grow = true, and fires the autosize event if the height changes.
37401      */
37402     autoSize : function(){
37403         if(!this.grow || !this.textSizeEl){
37404             return;
37405         }
37406         var el = this.el;
37407         var v = el.dom.value;
37408         var ts = this.textSizeEl;
37409
37410         ts.innerHTML = '';
37411         ts.appendChild(document.createTextNode(v));
37412         v = ts.innerHTML;
37413
37414         Roo.fly(ts).setWidth(this.el.getWidth());
37415         if(v.length < 1){
37416             v = "&#160;&#160;";
37417         }else{
37418             if(Roo.isIE){
37419                 v = v.replace(/\n/g, '<p>&#160;</p>');
37420             }
37421             v += "&#160;\n&#160;";
37422         }
37423         ts.innerHTML = v;
37424         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
37425         if(h != this.lastHeight){
37426             this.lastHeight = h;
37427             this.el.setHeight(h);
37428             this.fireEvent("autosize", this, h);
37429         }
37430     }
37431 });/*
37432  * Based on:
37433  * Ext JS Library 1.1.1
37434  * Copyright(c) 2006-2007, Ext JS, LLC.
37435  *
37436  * Originally Released Under LGPL - original licence link has changed is not relivant.
37437  *
37438  * Fork - LGPL
37439  * <script type="text/javascript">
37440  */
37441  
37442
37443 /**
37444  * @class Roo.form.NumberField
37445  * @extends Roo.form.TextField
37446  * Numeric text field that provides automatic keystroke filtering and numeric validation.
37447  * @constructor
37448  * Creates a new NumberField
37449  * @param {Object} config Configuration options
37450  */
37451 Roo.form.NumberField = function(config){
37452     Roo.form.NumberField.superclass.constructor.call(this, config);
37453 };
37454
37455 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
37456     /**
37457      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
37458      */
37459     fieldClass: "x-form-field x-form-num-field",
37460     /**
37461      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
37462      */
37463     allowDecimals : true,
37464     /**
37465      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
37466      */
37467     decimalSeparator : ".",
37468     /**
37469      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
37470      */
37471     decimalPrecision : 2,
37472     /**
37473      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
37474      */
37475     allowNegative : true,
37476     /**
37477      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
37478      */
37479     minValue : Number.NEGATIVE_INFINITY,
37480     /**
37481      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
37482      */
37483     maxValue : Number.MAX_VALUE,
37484     /**
37485      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37486      */
37487     minText : "The minimum value for this field is {0}",
37488     /**
37489      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37490      */
37491     maxText : "The maximum value for this field is {0}",
37492     /**
37493      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37494      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37495      */
37496     nanText : "{0} is not a valid number",
37497
37498     // private
37499     initEvents : function(){
37500         Roo.form.NumberField.superclass.initEvents.call(this);
37501         var allowed = "0123456789";
37502         if(this.allowDecimals){
37503             allowed += this.decimalSeparator;
37504         }
37505         if(this.allowNegative){
37506             allowed += "-";
37507         }
37508         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37509         var keyPress = function(e){
37510             var k = e.getKey();
37511             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37512                 return;
37513             }
37514             var c = e.getCharCode();
37515             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37516                 e.stopEvent();
37517             }
37518         };
37519         this.el.on("keypress", keyPress, this);
37520     },
37521
37522     // private
37523     validateValue : function(value){
37524         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
37525             return false;
37526         }
37527         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37528              return true;
37529         }
37530         var num = this.parseValue(value);
37531         if(isNaN(num)){
37532             this.markInvalid(String.format(this.nanText, value));
37533             return false;
37534         }
37535         if(num < this.minValue){
37536             this.markInvalid(String.format(this.minText, this.minValue));
37537             return false;
37538         }
37539         if(num > this.maxValue){
37540             this.markInvalid(String.format(this.maxText, this.maxValue));
37541             return false;
37542         }
37543         return true;
37544     },
37545
37546     getValue : function(){
37547         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
37548     },
37549
37550     // private
37551     parseValue : function(value){
37552         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37553         return isNaN(value) ? '' : value;
37554     },
37555
37556     // private
37557     fixPrecision : function(value){
37558         var nan = isNaN(value);
37559         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37560             return nan ? '' : value;
37561         }
37562         return parseFloat(value).toFixed(this.decimalPrecision);
37563     },
37564
37565     setValue : function(v){
37566         v = this.fixPrecision(v);
37567         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
37568     },
37569
37570     // private
37571     decimalPrecisionFcn : function(v){
37572         return Math.floor(v);
37573     },
37574
37575     beforeBlur : function(){
37576         var v = this.parseValue(this.getRawValue());
37577         if(v){
37578             this.setValue(v);
37579         }
37580     }
37581 });/*
37582  * Based on:
37583  * Ext JS Library 1.1.1
37584  * Copyright(c) 2006-2007, Ext JS, LLC.
37585  *
37586  * Originally Released Under LGPL - original licence link has changed is not relivant.
37587  *
37588  * Fork - LGPL
37589  * <script type="text/javascript">
37590  */
37591  
37592 /**
37593  * @class Roo.form.DateField
37594  * @extends Roo.form.TriggerField
37595  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37596 * @constructor
37597 * Create a new DateField
37598 * @param {Object} config
37599  */
37600 Roo.form.DateField = function(config){
37601     Roo.form.DateField.superclass.constructor.call(this, config);
37602     
37603       this.addEvents({
37604          
37605         /**
37606          * @event select
37607          * Fires when a date is selected
37608              * @param {Roo.form.DateField} combo This combo box
37609              * @param {Date} date The date selected
37610              */
37611         'select' : true
37612          
37613     });
37614     
37615     
37616     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37617     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37618     this.ddMatch = null;
37619     if(this.disabledDates){
37620         var dd = this.disabledDates;
37621         var re = "(?:";
37622         for(var i = 0; i < dd.length; i++){
37623             re += dd[i];
37624             if(i != dd.length-1) re += "|";
37625         }
37626         this.ddMatch = new RegExp(re + ")");
37627     }
37628 };
37629
37630 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
37631     /**
37632      * @cfg {String} format
37633      * The default date format string which can be overriden for localization support.  The format must be
37634      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37635      */
37636     format : "m/d/y",
37637     /**
37638      * @cfg {String} altFormats
37639      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37640      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37641      */
37642     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37643     /**
37644      * @cfg {Array} disabledDays
37645      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37646      */
37647     disabledDays : null,
37648     /**
37649      * @cfg {String} disabledDaysText
37650      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37651      */
37652     disabledDaysText : "Disabled",
37653     /**
37654      * @cfg {Array} disabledDates
37655      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37656      * expression so they are very powerful. Some examples:
37657      * <ul>
37658      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37659      * <li>["03/08", "09/16"] would disable those days for every year</li>
37660      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37661      * <li>["03/../2006"] would disable every day in March 2006</li>
37662      * <li>["^03"] would disable every day in every March</li>
37663      * </ul>
37664      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37665      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37666      */
37667     disabledDates : null,
37668     /**
37669      * @cfg {String} disabledDatesText
37670      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37671      */
37672     disabledDatesText : "Disabled",
37673     /**
37674      * @cfg {Date/String} minValue
37675      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37676      * valid format (defaults to null).
37677      */
37678     minValue : null,
37679     /**
37680      * @cfg {Date/String} maxValue
37681      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37682      * valid format (defaults to null).
37683      */
37684     maxValue : null,
37685     /**
37686      * @cfg {String} minText
37687      * The error text to display when the date in the cell is before minValue (defaults to
37688      * 'The date in this field must be after {minValue}').
37689      */
37690     minText : "The date in this field must be equal to or after {0}",
37691     /**
37692      * @cfg {String} maxText
37693      * The error text to display when the date in the cell is after maxValue (defaults to
37694      * 'The date in this field must be before {maxValue}').
37695      */
37696     maxText : "The date in this field must be equal to or before {0}",
37697     /**
37698      * @cfg {String} invalidText
37699      * The error text to display when the date in the field is invalid (defaults to
37700      * '{value} is not a valid date - it must be in the format {format}').
37701      */
37702     invalidText : "{0} is not a valid date - it must be in the format {1}",
37703     /**
37704      * @cfg {String} triggerClass
37705      * An additional CSS class used to style the trigger button.  The trigger will always get the
37706      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37707      * which displays a calendar icon).
37708      */
37709     triggerClass : 'x-form-date-trigger',
37710     
37711
37712     /**
37713      * @cfg {Boolean} useIso
37714      * if enabled, then the date field will use a hidden field to store the 
37715      * real value as iso formated date. default (false)
37716      */ 
37717     useIso : false,
37718     /**
37719      * @cfg {String/Object} autoCreate
37720      * A DomHelper element spec, or true for a default element spec (defaults to
37721      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37722      */ 
37723     // private
37724     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37725     
37726     // private
37727     hiddenField: false,
37728     
37729     onRender : function(ct, position)
37730     {
37731         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37732         if (this.useIso) {
37733             this.el.dom.removeAttribute('name'); 
37734             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37735                     'before', true);
37736             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
37737             // prevent input submission
37738             this.hiddenName = this.name;
37739         }
37740             
37741             
37742     },
37743     
37744     // private
37745     validateValue : function(value)
37746     {
37747         value = this.formatDate(value);
37748         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37749             Roo.log('super failed');
37750             return false;
37751         }
37752         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37753              return true;
37754         }
37755         var svalue = value;
37756         value = this.parseDate(value);
37757         if(!value){
37758             Roo.log('parse date failed' + svalue);
37759             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37760             return false;
37761         }
37762         var time = value.getTime();
37763         if(this.minValue && time < this.minValue.getTime()){
37764             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37765             return false;
37766         }
37767         if(this.maxValue && time > this.maxValue.getTime()){
37768             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37769             return false;
37770         }
37771         if(this.disabledDays){
37772             var day = value.getDay();
37773             for(var i = 0; i < this.disabledDays.length; i++) {
37774                 if(day === this.disabledDays[i]){
37775                     this.markInvalid(this.disabledDaysText);
37776                     return false;
37777                 }
37778             }
37779         }
37780         var fvalue = this.formatDate(value);
37781         if(this.ddMatch && this.ddMatch.test(fvalue)){
37782             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37783             return false;
37784         }
37785         return true;
37786     },
37787
37788     // private
37789     // Provides logic to override the default TriggerField.validateBlur which just returns true
37790     validateBlur : function(){
37791         return !this.menu || !this.menu.isVisible();
37792     },
37793
37794     /**
37795      * Returns the current date value of the date field.
37796      * @return {Date} The date value
37797      */
37798     getValue : function(){
37799         
37800         return  this.hiddenField ?
37801                 this.hiddenField.value :
37802                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37803     },
37804
37805     /**
37806      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37807      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37808      * (the default format used is "m/d/y").
37809      * <br />Usage:
37810      * <pre><code>
37811 //All of these calls set the same date value (May 4, 2006)
37812
37813 //Pass a date object:
37814 var dt = new Date('5/4/06');
37815 dateField.setValue(dt);
37816
37817 //Pass a date string (default format):
37818 dateField.setValue('5/4/06');
37819
37820 //Pass a date string (custom format):
37821 dateField.format = 'Y-m-d';
37822 dateField.setValue('2006-5-4');
37823 </code></pre>
37824      * @param {String/Date} date The date or valid date string
37825      */
37826     setValue : function(date){
37827         if (this.hiddenField) {
37828             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37829         }
37830         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37831         // make sure the value field is always stored as a date..
37832         this.value = this.parseDate(date);
37833         
37834         
37835     },
37836
37837     // private
37838     parseDate : function(value){
37839         if(!value || value instanceof Date){
37840             return value;
37841         }
37842         var v = Date.parseDate(value, this.format);
37843          if (!v && this.useIso) {
37844             v = Date.parseDate(value, 'Y-m-d');
37845         }
37846         if(!v && this.altFormats){
37847             if(!this.altFormatsArray){
37848                 this.altFormatsArray = this.altFormats.split("|");
37849             }
37850             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37851                 v = Date.parseDate(value, this.altFormatsArray[i]);
37852             }
37853         }
37854         return v;
37855     },
37856
37857     // private
37858     formatDate : function(date, fmt){
37859         return (!date || !(date instanceof Date)) ?
37860                date : date.dateFormat(fmt || this.format);
37861     },
37862
37863     // private
37864     menuListeners : {
37865         select: function(m, d){
37866             
37867             this.setValue(d);
37868             this.fireEvent('select', this, d);
37869         },
37870         show : function(){ // retain focus styling
37871             this.onFocus();
37872         },
37873         hide : function(){
37874             this.focus.defer(10, this);
37875             var ml = this.menuListeners;
37876             this.menu.un("select", ml.select,  this);
37877             this.menu.un("show", ml.show,  this);
37878             this.menu.un("hide", ml.hide,  this);
37879         }
37880     },
37881
37882     // private
37883     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37884     onTriggerClick : function(){
37885         if(this.disabled){
37886             return;
37887         }
37888         if(this.menu == null){
37889             this.menu = new Roo.menu.DateMenu();
37890         }
37891         Roo.apply(this.menu.picker,  {
37892             showClear: this.allowBlank,
37893             minDate : this.minValue,
37894             maxDate : this.maxValue,
37895             disabledDatesRE : this.ddMatch,
37896             disabledDatesText : this.disabledDatesText,
37897             disabledDays : this.disabledDays,
37898             disabledDaysText : this.disabledDaysText,
37899             format : this.useIso ? 'Y-m-d' : this.format,
37900             minText : String.format(this.minText, this.formatDate(this.minValue)),
37901             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37902         });
37903         this.menu.on(Roo.apply({}, this.menuListeners, {
37904             scope:this
37905         }));
37906         this.menu.picker.setValue(this.getValue() || new Date());
37907         this.menu.show(this.el, "tl-bl?");
37908     },
37909
37910     beforeBlur : function(){
37911         var v = this.parseDate(this.getRawValue());
37912         if(v){
37913             this.setValue(v);
37914         }
37915     }
37916
37917     /** @cfg {Boolean} grow @hide */
37918     /** @cfg {Number} growMin @hide */
37919     /** @cfg {Number} growMax @hide */
37920     /**
37921      * @hide
37922      * @method autoSize
37923      */
37924 });/*
37925  * Based on:
37926  * Ext JS Library 1.1.1
37927  * Copyright(c) 2006-2007, Ext JS, LLC.
37928  *
37929  * Originally Released Under LGPL - original licence link has changed is not relivant.
37930  *
37931  * Fork - LGPL
37932  * <script type="text/javascript">
37933  */
37934  
37935 /**
37936  * @class Roo.form.MonthField
37937  * @extends Roo.form.TriggerField
37938  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37939 * @constructor
37940 * Create a new MonthField
37941 * @param {Object} config
37942  */
37943 Roo.form.MonthField = function(config){
37944     
37945     Roo.form.MonthField.superclass.constructor.call(this, config);
37946     
37947       this.addEvents({
37948          
37949         /**
37950          * @event select
37951          * Fires when a date is selected
37952              * @param {Roo.form.MonthFieeld} combo This combo box
37953              * @param {Date} date The date selected
37954              */
37955         'select' : true
37956          
37957     });
37958     
37959     
37960     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37961     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37962     this.ddMatch = null;
37963     if(this.disabledDates){
37964         var dd = this.disabledDates;
37965         var re = "(?:";
37966         for(var i = 0; i < dd.length; i++){
37967             re += dd[i];
37968             if(i != dd.length-1) re += "|";
37969         }
37970         this.ddMatch = new RegExp(re + ")");
37971     }
37972 };
37973
37974 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
37975     /**
37976      * @cfg {String} format
37977      * The default date format string which can be overriden for localization support.  The format must be
37978      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37979      */
37980     format : "M Y",
37981     /**
37982      * @cfg {String} altFormats
37983      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37984      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37985      */
37986     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
37987     /**
37988      * @cfg {Array} disabledDays
37989      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37990      */
37991     disabledDays : [0,1,2,3,4,5,6],
37992     /**
37993      * @cfg {String} disabledDaysText
37994      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37995      */
37996     disabledDaysText : "Disabled",
37997     /**
37998      * @cfg {Array} disabledDates
37999      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38000      * expression so they are very powerful. Some examples:
38001      * <ul>
38002      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38003      * <li>["03/08", "09/16"] would disable those days for every year</li>
38004      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38005      * <li>["03/../2006"] would disable every day in March 2006</li>
38006      * <li>["^03"] would disable every day in every March</li>
38007      * </ul>
38008      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38009      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38010      */
38011     disabledDates : null,
38012     /**
38013      * @cfg {String} disabledDatesText
38014      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38015      */
38016     disabledDatesText : "Disabled",
38017     /**
38018      * @cfg {Date/String} minValue
38019      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38020      * valid format (defaults to null).
38021      */
38022     minValue : null,
38023     /**
38024      * @cfg {Date/String} maxValue
38025      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38026      * valid format (defaults to null).
38027      */
38028     maxValue : null,
38029     /**
38030      * @cfg {String} minText
38031      * The error text to display when the date in the cell is before minValue (defaults to
38032      * 'The date in this field must be after {minValue}').
38033      */
38034     minText : "The date in this field must be equal to or after {0}",
38035     /**
38036      * @cfg {String} maxTextf
38037      * The error text to display when the date in the cell is after maxValue (defaults to
38038      * 'The date in this field must be before {maxValue}').
38039      */
38040     maxText : "The date in this field must be equal to or before {0}",
38041     /**
38042      * @cfg {String} invalidText
38043      * The error text to display when the date in the field is invalid (defaults to
38044      * '{value} is not a valid date - it must be in the format {format}').
38045      */
38046     invalidText : "{0} is not a valid date - it must be in the format {1}",
38047     /**
38048      * @cfg {String} triggerClass
38049      * An additional CSS class used to style the trigger button.  The trigger will always get the
38050      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38051      * which displays a calendar icon).
38052      */
38053     triggerClass : 'x-form-date-trigger',
38054     
38055
38056     /**
38057      * @cfg {Boolean} useIso
38058      * if enabled, then the date field will use a hidden field to store the 
38059      * real value as iso formated date. default (true)
38060      */ 
38061     useIso : true,
38062     /**
38063      * @cfg {String/Object} autoCreate
38064      * A DomHelper element spec, or true for a default element spec (defaults to
38065      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38066      */ 
38067     // private
38068     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38069     
38070     // private
38071     hiddenField: false,
38072     
38073     hideMonthPicker : false,
38074     
38075     onRender : function(ct, position)
38076     {
38077         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38078         if (this.useIso) {
38079             this.el.dom.removeAttribute('name'); 
38080             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38081                     'before', true);
38082             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38083             // prevent input submission
38084             this.hiddenName = this.name;
38085         }
38086             
38087             
38088     },
38089     
38090     // private
38091     validateValue : function(value)
38092     {
38093         value = this.formatDate(value);
38094         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38095             return false;
38096         }
38097         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38098              return true;
38099         }
38100         var svalue = value;
38101         value = this.parseDate(value);
38102         if(!value){
38103             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38104             return false;
38105         }
38106         var time = value.getTime();
38107         if(this.minValue && time < this.minValue.getTime()){
38108             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38109             return false;
38110         }
38111         if(this.maxValue && time > this.maxValue.getTime()){
38112             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38113             return false;
38114         }
38115         /*if(this.disabledDays){
38116             var day = value.getDay();
38117             for(var i = 0; i < this.disabledDays.length; i++) {
38118                 if(day === this.disabledDays[i]){
38119                     this.markInvalid(this.disabledDaysText);
38120                     return false;
38121                 }
38122             }
38123         }
38124         */
38125         var fvalue = this.formatDate(value);
38126         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38127             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38128             return false;
38129         }
38130         */
38131         return true;
38132     },
38133
38134     // private
38135     // Provides logic to override the default TriggerField.validateBlur which just returns true
38136     validateBlur : function(){
38137         return !this.menu || !this.menu.isVisible();
38138     },
38139
38140     /**
38141      * Returns the current date value of the date field.
38142      * @return {Date} The date value
38143      */
38144     getValue : function(){
38145         
38146         
38147         
38148         return  this.hiddenField ?
38149                 this.hiddenField.value :
38150                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38151     },
38152
38153     /**
38154      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38155      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38156      * (the default format used is "m/d/y").
38157      * <br />Usage:
38158      * <pre><code>
38159 //All of these calls set the same date value (May 4, 2006)
38160
38161 //Pass a date object:
38162 var dt = new Date('5/4/06');
38163 monthField.setValue(dt);
38164
38165 //Pass a date string (default format):
38166 monthField.setValue('5/4/06');
38167
38168 //Pass a date string (custom format):
38169 monthField.format = 'Y-m-d';
38170 monthField.setValue('2006-5-4');
38171 </code></pre>
38172      * @param {String/Date} date The date or valid date string
38173      */
38174     setValue : function(date){
38175         Roo.log('month setValue' + date);
38176         // can only be first of month..
38177         
38178         var val = this.parseDate(date);
38179         
38180         if (this.hiddenField) {
38181             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38182         }
38183         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38184         this.value = this.parseDate(date);
38185     },
38186
38187     // private
38188     parseDate : function(value){
38189         if(!value || value instanceof Date){
38190             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38191             return value;
38192         }
38193         var v = Date.parseDate(value, this.format);
38194         if (!v && this.useIso) {
38195             v = Date.parseDate(value, 'Y-m-d');
38196         }
38197         if (v) {
38198             // 
38199             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38200         }
38201         
38202         
38203         if(!v && this.altFormats){
38204             if(!this.altFormatsArray){
38205                 this.altFormatsArray = this.altFormats.split("|");
38206             }
38207             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38208                 v = Date.parseDate(value, this.altFormatsArray[i]);
38209             }
38210         }
38211         return v;
38212     },
38213
38214     // private
38215     formatDate : function(date, fmt){
38216         return (!date || !(date instanceof Date)) ?
38217                date : date.dateFormat(fmt || this.format);
38218     },
38219
38220     // private
38221     menuListeners : {
38222         select: function(m, d){
38223             this.setValue(d);
38224             this.fireEvent('select', this, d);
38225         },
38226         show : function(){ // retain focus styling
38227             this.onFocus();
38228         },
38229         hide : function(){
38230             this.focus.defer(10, this);
38231             var ml = this.menuListeners;
38232             this.menu.un("select", ml.select,  this);
38233             this.menu.un("show", ml.show,  this);
38234             this.menu.un("hide", ml.hide,  this);
38235         }
38236     },
38237     // private
38238     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38239     onTriggerClick : function(){
38240         if(this.disabled){
38241             return;
38242         }
38243         if(this.menu == null){
38244             this.menu = new Roo.menu.DateMenu();
38245            
38246         }
38247         
38248         Roo.apply(this.menu.picker,  {
38249             
38250             showClear: this.allowBlank,
38251             minDate : this.minValue,
38252             maxDate : this.maxValue,
38253             disabledDatesRE : this.ddMatch,
38254             disabledDatesText : this.disabledDatesText,
38255             
38256             format : this.useIso ? 'Y-m-d' : this.format,
38257             minText : String.format(this.minText, this.formatDate(this.minValue)),
38258             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38259             
38260         });
38261          this.menu.on(Roo.apply({}, this.menuListeners, {
38262             scope:this
38263         }));
38264        
38265         
38266         var m = this.menu;
38267         var p = m.picker;
38268         
38269         // hide month picker get's called when we called by 'before hide';
38270         
38271         var ignorehide = true;
38272         p.hideMonthPicker  = function(disableAnim){
38273             if (ignorehide) {
38274                 return;
38275             }
38276              if(this.monthPicker){
38277                 Roo.log("hideMonthPicker called");
38278                 if(disableAnim === true){
38279                     this.monthPicker.hide();
38280                 }else{
38281                     this.monthPicker.slideOut('t', {duration:.2});
38282                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38283                     p.fireEvent("select", this, this.value);
38284                     m.hide();
38285                 }
38286             }
38287         }
38288         
38289         Roo.log('picker set value');
38290         Roo.log(this.getValue());
38291         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38292         m.show(this.el, 'tl-bl?');
38293         ignorehide  = false;
38294         // this will trigger hideMonthPicker..
38295         
38296         
38297         // hidden the day picker
38298         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38299         
38300         
38301         
38302       
38303         
38304         p.showMonthPicker.defer(100, p);
38305     
38306         
38307        
38308     },
38309
38310     beforeBlur : function(){
38311         var v = this.parseDate(this.getRawValue());
38312         if(v){
38313             this.setValue(v);
38314         }
38315     }
38316
38317     /** @cfg {Boolean} grow @hide */
38318     /** @cfg {Number} growMin @hide */
38319     /** @cfg {Number} growMax @hide */
38320     /**
38321      * @hide
38322      * @method autoSize
38323      */
38324 });/*
38325  * Based on:
38326  * Ext JS Library 1.1.1
38327  * Copyright(c) 2006-2007, Ext JS, LLC.
38328  *
38329  * Originally Released Under LGPL - original licence link has changed is not relivant.
38330  *
38331  * Fork - LGPL
38332  * <script type="text/javascript">
38333  */
38334  
38335
38336 /**
38337  * @class Roo.form.ComboBox
38338  * @extends Roo.form.TriggerField
38339  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
38340  * @constructor
38341  * Create a new ComboBox.
38342  * @param {Object} config Configuration options
38343  */
38344 Roo.form.ComboBox = function(config){
38345     Roo.form.ComboBox.superclass.constructor.call(this, config);
38346     this.addEvents({
38347         /**
38348          * @event expand
38349          * Fires when the dropdown list is expanded
38350              * @param {Roo.form.ComboBox} combo This combo box
38351              */
38352         'expand' : true,
38353         /**
38354          * @event collapse
38355          * Fires when the dropdown list is collapsed
38356              * @param {Roo.form.ComboBox} combo This combo box
38357              */
38358         'collapse' : true,
38359         /**
38360          * @event beforeselect
38361          * Fires before a list item is selected. Return false to cancel the selection.
38362              * @param {Roo.form.ComboBox} combo This combo box
38363              * @param {Roo.data.Record} record The data record returned from the underlying store
38364              * @param {Number} index The index of the selected item in the dropdown list
38365              */
38366         'beforeselect' : true,
38367         /**
38368          * @event select
38369          * Fires when a list item is selected
38370              * @param {Roo.form.ComboBox} combo This combo box
38371              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
38372              * @param {Number} index The index of the selected item in the dropdown list
38373              */
38374         'select' : true,
38375         /**
38376          * @event beforequery
38377          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
38378          * The event object passed has these properties:
38379              * @param {Roo.form.ComboBox} combo This combo box
38380              * @param {String} query The query
38381              * @param {Boolean} forceAll true to force "all" query
38382              * @param {Boolean} cancel true to cancel the query
38383              * @param {Object} e The query event object
38384              */
38385         'beforequery': true,
38386          /**
38387          * @event add
38388          * Fires when the 'add' icon is pressed (add a listener to enable add button)
38389              * @param {Roo.form.ComboBox} combo This combo box
38390              */
38391         'add' : true,
38392         /**
38393          * @event edit
38394          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
38395              * @param {Roo.form.ComboBox} combo This combo box
38396              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
38397              */
38398         'edit' : true
38399         
38400         
38401     });
38402     if(this.transform){
38403         this.allowDomMove = false;
38404         var s = Roo.getDom(this.transform);
38405         if(!this.hiddenName){
38406             this.hiddenName = s.name;
38407         }
38408         if(!this.store){
38409             this.mode = 'local';
38410             var d = [], opts = s.options;
38411             for(var i = 0, len = opts.length;i < len; i++){
38412                 var o = opts[i];
38413                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
38414                 if(o.selected) {
38415                     this.value = value;
38416                 }
38417                 d.push([value, o.text]);
38418             }
38419             this.store = new Roo.data.SimpleStore({
38420                 'id': 0,
38421                 fields: ['value', 'text'],
38422                 data : d
38423             });
38424             this.valueField = 'value';
38425             this.displayField = 'text';
38426         }
38427         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
38428         if(!this.lazyRender){
38429             this.target = true;
38430             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
38431             s.parentNode.removeChild(s); // remove it
38432             this.render(this.el.parentNode);
38433         }else{
38434             s.parentNode.removeChild(s); // remove it
38435         }
38436
38437     }
38438     if (this.store) {
38439         this.store = Roo.factory(this.store, Roo.data);
38440     }
38441     
38442     this.selectedIndex = -1;
38443     if(this.mode == 'local'){
38444         if(config.queryDelay === undefined){
38445             this.queryDelay = 10;
38446         }
38447         if(config.minChars === undefined){
38448             this.minChars = 0;
38449         }
38450     }
38451 };
38452
38453 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
38454     /**
38455      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
38456      */
38457     /**
38458      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
38459      * rendering into an Roo.Editor, defaults to false)
38460      */
38461     /**
38462      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
38463      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
38464      */
38465     /**
38466      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
38467      */
38468     /**
38469      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
38470      * the dropdown list (defaults to undefined, with no header element)
38471      */
38472
38473      /**
38474      * @cfg {String/Roo.Template} tpl The template to use to render the output
38475      */
38476      
38477     // private
38478     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
38479     /**
38480      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
38481      */
38482     listWidth: undefined,
38483     /**
38484      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
38485      * mode = 'remote' or 'text' if mode = 'local')
38486      */
38487     displayField: undefined,
38488     /**
38489      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
38490      * mode = 'remote' or 'value' if mode = 'local'). 
38491      * Note: use of a valueField requires the user make a selection
38492      * in order for a value to be mapped.
38493      */
38494     valueField: undefined,
38495     
38496     
38497     /**
38498      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
38499      * field's data value (defaults to the underlying DOM element's name)
38500      */
38501     hiddenName: undefined,
38502     /**
38503      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
38504      */
38505     listClass: '',
38506     /**
38507      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
38508      */
38509     selectedClass: 'x-combo-selected',
38510     /**
38511      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38512      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
38513      * which displays a downward arrow icon).
38514      */
38515     triggerClass : 'x-form-arrow-trigger',
38516     /**
38517      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
38518      */
38519     shadow:'sides',
38520     /**
38521      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
38522      * anchor positions (defaults to 'tl-bl')
38523      */
38524     listAlign: 'tl-bl?',
38525     /**
38526      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
38527      */
38528     maxHeight: 300,
38529     /**
38530      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
38531      * query specified by the allQuery config option (defaults to 'query')
38532      */
38533     triggerAction: 'query',
38534     /**
38535      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
38536      * (defaults to 4, does not apply if editable = false)
38537      */
38538     minChars : 4,
38539     /**
38540      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
38541      * delay (typeAheadDelay) if it matches a known value (defaults to false)
38542      */
38543     typeAhead: false,
38544     /**
38545      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
38546      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
38547      */
38548     queryDelay: 500,
38549     /**
38550      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
38551      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
38552      */
38553     pageSize: 0,
38554     /**
38555      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
38556      * when editable = true (defaults to false)
38557      */
38558     selectOnFocus:false,
38559     /**
38560      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
38561      */
38562     queryParam: 'query',
38563     /**
38564      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
38565      * when mode = 'remote' (defaults to 'Loading...')
38566      */
38567     loadingText: 'Loading...',
38568     /**
38569      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
38570      */
38571     resizable: false,
38572     /**
38573      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
38574      */
38575     handleHeight : 8,
38576     /**
38577      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
38578      * traditional select (defaults to true)
38579      */
38580     editable: true,
38581     /**
38582      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
38583      */
38584     allQuery: '',
38585     /**
38586      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
38587      */
38588     mode: 'remote',
38589     /**
38590      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
38591      * listWidth has a higher value)
38592      */
38593     minListWidth : 70,
38594     /**
38595      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
38596      * allow the user to set arbitrary text into the field (defaults to false)
38597      */
38598     forceSelection:false,
38599     /**
38600      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
38601      * if typeAhead = true (defaults to 250)
38602      */
38603     typeAheadDelay : 250,
38604     /**
38605      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
38606      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
38607      */
38608     valueNotFoundText : undefined,
38609     /**
38610      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
38611      */
38612     blockFocus : false,
38613     
38614     /**
38615      * @cfg {Boolean} disableClear Disable showing of clear button.
38616      */
38617     disableClear : false,
38618     /**
38619      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
38620      */
38621     alwaysQuery : false,
38622     
38623     //private
38624     addicon : false,
38625     editicon: false,
38626     
38627     // element that contains real text value.. (when hidden is used..)
38628      
38629     // private
38630     onRender : function(ct, position){
38631         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
38632         if(this.hiddenName){
38633             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
38634                     'before', true);
38635             this.hiddenField.value =
38636                 this.hiddenValue !== undefined ? this.hiddenValue :
38637                 this.value !== undefined ? this.value : '';
38638
38639             // prevent input submission
38640             this.el.dom.removeAttribute('name');
38641              
38642              
38643         }
38644         if(Roo.isGecko){
38645             this.el.dom.setAttribute('autocomplete', 'off');
38646         }
38647
38648         var cls = 'x-combo-list';
38649
38650         this.list = new Roo.Layer({
38651             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
38652         });
38653
38654         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
38655         this.list.setWidth(lw);
38656         this.list.swallowEvent('mousewheel');
38657         this.assetHeight = 0;
38658
38659         if(this.title){
38660             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
38661             this.assetHeight += this.header.getHeight();
38662         }
38663
38664         this.innerList = this.list.createChild({cls:cls+'-inner'});
38665         this.innerList.on('mouseover', this.onViewOver, this);
38666         this.innerList.on('mousemove', this.onViewMove, this);
38667         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38668         
38669         if(this.allowBlank && !this.pageSize && !this.disableClear){
38670             this.footer = this.list.createChild({cls:cls+'-ft'});
38671             this.pageTb = new Roo.Toolbar(this.footer);
38672            
38673         }
38674         if(this.pageSize){
38675             this.footer = this.list.createChild({cls:cls+'-ft'});
38676             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
38677                     {pageSize: this.pageSize});
38678             
38679         }
38680         
38681         if (this.pageTb && this.allowBlank && !this.disableClear) {
38682             var _this = this;
38683             this.pageTb.add(new Roo.Toolbar.Fill(), {
38684                 cls: 'x-btn-icon x-btn-clear',
38685                 text: '&#160;',
38686                 handler: function()
38687                 {
38688                     _this.collapse();
38689                     _this.clearValue();
38690                     _this.onSelect(false, -1);
38691                 }
38692             });
38693         }
38694         if (this.footer) {
38695             this.assetHeight += this.footer.getHeight();
38696         }
38697         
38698
38699         if(!this.tpl){
38700             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
38701         }
38702
38703         this.view = new Roo.View(this.innerList, this.tpl, {
38704             singleSelect:true, store: this.store, selectedClass: this.selectedClass
38705         });
38706
38707         this.view.on('click', this.onViewClick, this);
38708
38709         this.store.on('beforeload', this.onBeforeLoad, this);
38710         this.store.on('load', this.onLoad, this);
38711         this.store.on('loadexception', this.onLoadException, this);
38712
38713         if(this.resizable){
38714             this.resizer = new Roo.Resizable(this.list,  {
38715                pinned:true, handles:'se'
38716             });
38717             this.resizer.on('resize', function(r, w, h){
38718                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
38719                 this.listWidth = w;
38720                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
38721                 this.restrictHeight();
38722             }, this);
38723             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
38724         }
38725         if(!this.editable){
38726             this.editable = true;
38727             this.setEditable(false);
38728         }  
38729         
38730         
38731         if (typeof(this.events.add.listeners) != 'undefined') {
38732             
38733             this.addicon = this.wrap.createChild(
38734                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
38735        
38736             this.addicon.on('click', function(e) {
38737                 this.fireEvent('add', this);
38738             }, this);
38739         }
38740         if (typeof(this.events.edit.listeners) != 'undefined') {
38741             
38742             this.editicon = this.wrap.createChild(
38743                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
38744             if (this.addicon) {
38745                 this.editicon.setStyle('margin-left', '40px');
38746             }
38747             this.editicon.on('click', function(e) {
38748                 
38749                 // we fire even  if inothing is selected..
38750                 this.fireEvent('edit', this, this.lastData );
38751                 
38752             }, this);
38753         }
38754         
38755         
38756         
38757     },
38758
38759     // private
38760     initEvents : function(){
38761         Roo.form.ComboBox.superclass.initEvents.call(this);
38762
38763         this.keyNav = new Roo.KeyNav(this.el, {
38764             "up" : function(e){
38765                 this.inKeyMode = true;
38766                 this.selectPrev();
38767             },
38768
38769             "down" : function(e){
38770                 if(!this.isExpanded()){
38771                     this.onTriggerClick();
38772                 }else{
38773                     this.inKeyMode = true;
38774                     this.selectNext();
38775                 }
38776             },
38777
38778             "enter" : function(e){
38779                 this.onViewClick();
38780                 //return true;
38781             },
38782
38783             "esc" : function(e){
38784                 this.collapse();
38785             },
38786
38787             "tab" : function(e){
38788                 this.onViewClick(false);
38789                 this.fireEvent("specialkey", this, e);
38790                 return true;
38791             },
38792
38793             scope : this,
38794
38795             doRelay : function(foo, bar, hname){
38796                 if(hname == 'down' || this.scope.isExpanded()){
38797                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38798                 }
38799                 return true;
38800             },
38801
38802             forceKeyDown: true
38803         });
38804         this.queryDelay = Math.max(this.queryDelay || 10,
38805                 this.mode == 'local' ? 10 : 250);
38806         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
38807         if(this.typeAhead){
38808             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
38809         }
38810         if(this.editable !== false){
38811             this.el.on("keyup", this.onKeyUp, this);
38812         }
38813         if(this.forceSelection){
38814             this.on('blur', this.doForce, this);
38815         }
38816     },
38817
38818     onDestroy : function(){
38819         if(this.view){
38820             this.view.setStore(null);
38821             this.view.el.removeAllListeners();
38822             this.view.el.remove();
38823             this.view.purgeListeners();
38824         }
38825         if(this.list){
38826             this.list.destroy();
38827         }
38828         if(this.store){
38829             this.store.un('beforeload', this.onBeforeLoad, this);
38830             this.store.un('load', this.onLoad, this);
38831             this.store.un('loadexception', this.onLoadException, this);
38832         }
38833         Roo.form.ComboBox.superclass.onDestroy.call(this);
38834     },
38835
38836     // private
38837     fireKey : function(e){
38838         if(e.isNavKeyPress() && !this.list.isVisible()){
38839             this.fireEvent("specialkey", this, e);
38840         }
38841     },
38842
38843     // private
38844     onResize: function(w, h){
38845         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
38846         
38847         if(typeof w != 'number'){
38848             // we do not handle it!?!?
38849             return;
38850         }
38851         var tw = this.trigger.getWidth();
38852         tw += this.addicon ? this.addicon.getWidth() : 0;
38853         tw += this.editicon ? this.editicon.getWidth() : 0;
38854         var x = w - tw;
38855         this.el.setWidth( this.adjustWidth('input', x));
38856             
38857         this.trigger.setStyle('left', x+'px');
38858         
38859         if(this.list && this.listWidth === undefined){
38860             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
38861             this.list.setWidth(lw);
38862             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38863         }
38864         
38865     
38866         
38867     },
38868
38869     /**
38870      * Allow or prevent the user from directly editing the field text.  If false is passed,
38871      * the user will only be able to select from the items defined in the dropdown list.  This method
38872      * is the runtime equivalent of setting the 'editable' config option at config time.
38873      * @param {Boolean} value True to allow the user to directly edit the field text
38874      */
38875     setEditable : function(value){
38876         if(value == this.editable){
38877             return;
38878         }
38879         this.editable = value;
38880         if(!value){
38881             this.el.dom.setAttribute('readOnly', true);
38882             this.el.on('mousedown', this.onTriggerClick,  this);
38883             this.el.addClass('x-combo-noedit');
38884         }else{
38885             this.el.dom.setAttribute('readOnly', false);
38886             this.el.un('mousedown', this.onTriggerClick,  this);
38887             this.el.removeClass('x-combo-noedit');
38888         }
38889     },
38890
38891     // private
38892     onBeforeLoad : function(){
38893         if(!this.hasFocus){
38894             return;
38895         }
38896         this.innerList.update(this.loadingText ?
38897                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
38898         this.restrictHeight();
38899         this.selectedIndex = -1;
38900     },
38901
38902     // private
38903     onLoad : function(){
38904         if(!this.hasFocus){
38905             return;
38906         }
38907         if(this.store.getCount() > 0){
38908             this.expand();
38909             this.restrictHeight();
38910             if(this.lastQuery == this.allQuery){
38911                 if(this.editable){
38912                     this.el.dom.select();
38913                 }
38914                 if(!this.selectByValue(this.value, true)){
38915                     this.select(0, true);
38916                 }
38917             }else{
38918                 this.selectNext();
38919                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
38920                     this.taTask.delay(this.typeAheadDelay);
38921                 }
38922             }
38923         }else{
38924             this.onEmptyResults();
38925         }
38926         //this.el.focus();
38927     },
38928     // private
38929     onLoadException : function()
38930     {
38931         this.collapse();
38932         Roo.log(this.store.reader.jsonData);
38933         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38934             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38935         }
38936         
38937         
38938     },
38939     // private
38940     onTypeAhead : function(){
38941         if(this.store.getCount() > 0){
38942             var r = this.store.getAt(0);
38943             var newValue = r.data[this.displayField];
38944             var len = newValue.length;
38945             var selStart = this.getRawValue().length;
38946             if(selStart != len){
38947                 this.setRawValue(newValue);
38948                 this.selectText(selStart, newValue.length);
38949             }
38950         }
38951     },
38952
38953     // private
38954     onSelect : function(record, index){
38955         if(this.fireEvent('beforeselect', this, record, index) !== false){
38956             this.setFromData(index > -1 ? record.data : false);
38957             this.collapse();
38958             this.fireEvent('select', this, record, index);
38959         }
38960     },
38961
38962     /**
38963      * Returns the currently selected field value or empty string if no value is set.
38964      * @return {String} value The selected value
38965      */
38966     getValue : function(){
38967         if(this.valueField){
38968             return typeof this.value != 'undefined' ? this.value : '';
38969         }else{
38970             return Roo.form.ComboBox.superclass.getValue.call(this);
38971         }
38972     },
38973
38974     /**
38975      * Clears any text/value currently set in the field
38976      */
38977     clearValue : function(){
38978         if(this.hiddenField){
38979             this.hiddenField.value = '';
38980         }
38981         this.value = '';
38982         this.setRawValue('');
38983         this.lastSelectionText = '';
38984         this.applyEmptyText();
38985     },
38986
38987     /**
38988      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
38989      * will be displayed in the field.  If the value does not match the data value of an existing item,
38990      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
38991      * Otherwise the field will be blank (although the value will still be set).
38992      * @param {String} value The value to match
38993      */
38994     setValue : function(v){
38995         var text = v;
38996         if(this.valueField){
38997             var r = this.findRecord(this.valueField, v);
38998             if(r){
38999                 text = r.data[this.displayField];
39000             }else if(this.valueNotFoundText !== undefined){
39001                 text = this.valueNotFoundText;
39002             }
39003         }
39004         this.lastSelectionText = text;
39005         if(this.hiddenField){
39006             this.hiddenField.value = v;
39007         }
39008         Roo.form.ComboBox.superclass.setValue.call(this, text);
39009         this.value = v;
39010     },
39011     /**
39012      * @property {Object} the last set data for the element
39013      */
39014     
39015     lastData : false,
39016     /**
39017      * Sets the value of the field based on a object which is related to the record format for the store.
39018      * @param {Object} value the value to set as. or false on reset?
39019      */
39020     setFromData : function(o){
39021         var dv = ''; // display value
39022         var vv = ''; // value value..
39023         this.lastData = o;
39024         if (this.displayField) {
39025             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39026         } else {
39027             // this is an error condition!!!
39028             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39029         }
39030         
39031         if(this.valueField){
39032             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39033         }
39034         if(this.hiddenField){
39035             this.hiddenField.value = vv;
39036             
39037             this.lastSelectionText = dv;
39038             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39039             this.value = vv;
39040             return;
39041         }
39042         // no hidden field.. - we store the value in 'value', but still display
39043         // display field!!!!
39044         this.lastSelectionText = dv;
39045         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39046         this.value = vv;
39047         
39048         
39049     },
39050     // private
39051     reset : function(){
39052         // overridden so that last data is reset..
39053         this.setValue(this.originalValue);
39054         this.clearInvalid();
39055         this.lastData = false;
39056         if (this.view) {
39057             this.view.clearSelections();
39058         }
39059     },
39060     // private
39061     findRecord : function(prop, value){
39062         var record;
39063         if(this.store.getCount() > 0){
39064             this.store.each(function(r){
39065                 if(r.data[prop] == value){
39066                     record = r;
39067                     return false;
39068                 }
39069                 return true;
39070             });
39071         }
39072         return record;
39073     },
39074     
39075     getName: function()
39076     {
39077         // returns hidden if it's set..
39078         if (!this.rendered) {return ''};
39079         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39080         
39081     },
39082     // private
39083     onViewMove : function(e, t){
39084         this.inKeyMode = false;
39085     },
39086
39087     // private
39088     onViewOver : function(e, t){
39089         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39090             return;
39091         }
39092         var item = this.view.findItemFromChild(t);
39093         if(item){
39094             var index = this.view.indexOf(item);
39095             this.select(index, false);
39096         }
39097     },
39098
39099     // private
39100     onViewClick : function(doFocus)
39101     {
39102         var index = this.view.getSelectedIndexes()[0];
39103         var r = this.store.getAt(index);
39104         if(r){
39105             this.onSelect(r, index);
39106         }
39107         if(doFocus !== false && !this.blockFocus){
39108             this.el.focus();
39109         }
39110     },
39111
39112     // private
39113     restrictHeight : function(){
39114         this.innerList.dom.style.height = '';
39115         var inner = this.innerList.dom;
39116         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39117         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39118         this.list.beginUpdate();
39119         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39120         this.list.alignTo(this.el, this.listAlign);
39121         this.list.endUpdate();
39122     },
39123
39124     // private
39125     onEmptyResults : function(){
39126         this.collapse();
39127     },
39128
39129     /**
39130      * Returns true if the dropdown list is expanded, else false.
39131      */
39132     isExpanded : function(){
39133         return this.list.isVisible();
39134     },
39135
39136     /**
39137      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39138      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39139      * @param {String} value The data value of the item to select
39140      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39141      * selected item if it is not currently in view (defaults to true)
39142      * @return {Boolean} True if the value matched an item in the list, else false
39143      */
39144     selectByValue : function(v, scrollIntoView){
39145         if(v !== undefined && v !== null){
39146             var r = this.findRecord(this.valueField || this.displayField, v);
39147             if(r){
39148                 this.select(this.store.indexOf(r), scrollIntoView);
39149                 return true;
39150             }
39151         }
39152         return false;
39153     },
39154
39155     /**
39156      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39157      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39158      * @param {Number} index The zero-based index of the list item to select
39159      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39160      * selected item if it is not currently in view (defaults to true)
39161      */
39162     select : function(index, scrollIntoView){
39163         this.selectedIndex = index;
39164         this.view.select(index);
39165         if(scrollIntoView !== false){
39166             var el = this.view.getNode(index);
39167             if(el){
39168                 this.innerList.scrollChildIntoView(el, false);
39169             }
39170         }
39171     },
39172
39173     // private
39174     selectNext : function(){
39175         var ct = this.store.getCount();
39176         if(ct > 0){
39177             if(this.selectedIndex == -1){
39178                 this.select(0);
39179             }else if(this.selectedIndex < ct-1){
39180                 this.select(this.selectedIndex+1);
39181             }
39182         }
39183     },
39184
39185     // private
39186     selectPrev : function(){
39187         var ct = this.store.getCount();
39188         if(ct > 0){
39189             if(this.selectedIndex == -1){
39190                 this.select(0);
39191             }else if(this.selectedIndex != 0){
39192                 this.select(this.selectedIndex-1);
39193             }
39194         }
39195     },
39196
39197     // private
39198     onKeyUp : function(e){
39199         if(this.editable !== false && !e.isSpecialKey()){
39200             this.lastKey = e.getKey();
39201             this.dqTask.delay(this.queryDelay);
39202         }
39203     },
39204
39205     // private
39206     validateBlur : function(){
39207         return !this.list || !this.list.isVisible();   
39208     },
39209
39210     // private
39211     initQuery : function(){
39212         this.doQuery(this.getRawValue());
39213     },
39214
39215     // private
39216     doForce : function(){
39217         if(this.el.dom.value.length > 0){
39218             this.el.dom.value =
39219                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39220             this.applyEmptyText();
39221         }
39222     },
39223
39224     /**
39225      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39226      * query allowing the query action to be canceled if needed.
39227      * @param {String} query The SQL query to execute
39228      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39229      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39230      * saved in the current store (defaults to false)
39231      */
39232     doQuery : function(q, forceAll){
39233         if(q === undefined || q === null){
39234             q = '';
39235         }
39236         var qe = {
39237             query: q,
39238             forceAll: forceAll,
39239             combo: this,
39240             cancel:false
39241         };
39242         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39243             return false;
39244         }
39245         q = qe.query;
39246         forceAll = qe.forceAll;
39247         if(forceAll === true || (q.length >= this.minChars)){
39248             if(this.lastQuery != q || this.alwaysQuery){
39249                 this.lastQuery = q;
39250                 if(this.mode == 'local'){
39251                     this.selectedIndex = -1;
39252                     if(forceAll){
39253                         this.store.clearFilter();
39254                     }else{
39255                         this.store.filter(this.displayField, q);
39256                     }
39257                     this.onLoad();
39258                 }else{
39259                     this.store.baseParams[this.queryParam] = q;
39260                     this.store.load({
39261                         params: this.getParams(q)
39262                     });
39263                     this.expand();
39264                 }
39265             }else{
39266                 this.selectedIndex = -1;
39267                 this.onLoad();   
39268             }
39269         }
39270     },
39271
39272     // private
39273     getParams : function(q){
39274         var p = {};
39275         //p[this.queryParam] = q;
39276         if(this.pageSize){
39277             p.start = 0;
39278             p.limit = this.pageSize;
39279         }
39280         return p;
39281     },
39282
39283     /**
39284      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39285      */
39286     collapse : function(){
39287         if(!this.isExpanded()){
39288             return;
39289         }
39290         this.list.hide();
39291         Roo.get(document).un('mousedown', this.collapseIf, this);
39292         Roo.get(document).un('mousewheel', this.collapseIf, this);
39293         if (!this.editable) {
39294             Roo.get(document).un('keydown', this.listKeyPress, this);
39295         }
39296         this.fireEvent('collapse', this);
39297     },
39298
39299     // private
39300     collapseIf : function(e){
39301         if(!e.within(this.wrap) && !e.within(this.list)){
39302             this.collapse();
39303         }
39304     },
39305
39306     /**
39307      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39308      */
39309     expand : function(){
39310         if(this.isExpanded() || !this.hasFocus){
39311             return;
39312         }
39313         this.list.alignTo(this.el, this.listAlign);
39314         this.list.show();
39315         Roo.get(document).on('mousedown', this.collapseIf, this);
39316         Roo.get(document).on('mousewheel', this.collapseIf, this);
39317         if (!this.editable) {
39318             Roo.get(document).on('keydown', this.listKeyPress, this);
39319         }
39320         
39321         this.fireEvent('expand', this);
39322     },
39323
39324     // private
39325     // Implements the default empty TriggerField.onTriggerClick function
39326     onTriggerClick : function(){
39327         if(this.disabled){
39328             return;
39329         }
39330         if(this.isExpanded()){
39331             this.collapse();
39332             if (!this.blockFocus) {
39333                 this.el.focus();
39334             }
39335             
39336         }else {
39337             this.hasFocus = true;
39338             if(this.triggerAction == 'all') {
39339                 this.doQuery(this.allQuery, true);
39340             } else {
39341                 this.doQuery(this.getRawValue());
39342             }
39343             if (!this.blockFocus) {
39344                 this.el.focus();
39345             }
39346         }
39347     },
39348     listKeyPress : function(e)
39349     {
39350         //Roo.log('listkeypress');
39351         // scroll to first matching element based on key pres..
39352         if (e.isSpecialKey()) {
39353             return false;
39354         }
39355         var k = String.fromCharCode(e.getKey()).toUpperCase();
39356         //Roo.log(k);
39357         var match  = false;
39358         var csel = this.view.getSelectedNodes();
39359         var cselitem = false;
39360         if (csel.length) {
39361             var ix = this.view.indexOf(csel[0]);
39362             cselitem  = this.store.getAt(ix);
39363             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
39364                 cselitem = false;
39365             }
39366             
39367         }
39368         
39369         this.store.each(function(v) { 
39370             if (cselitem) {
39371                 // start at existing selection.
39372                 if (cselitem.id == v.id) {
39373                     cselitem = false;
39374                 }
39375                 return;
39376             }
39377                 
39378             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
39379                 match = this.store.indexOf(v);
39380                 return false;
39381             }
39382         }, this);
39383         
39384         if (match === false) {
39385             return true; // no more action?
39386         }
39387         // scroll to?
39388         this.view.select(match);
39389         var sn = Roo.get(this.view.getSelectedNodes()[0])
39390         sn.scrollIntoView(sn.dom.parentNode, false);
39391     }
39392
39393     /** 
39394     * @cfg {Boolean} grow 
39395     * @hide 
39396     */
39397     /** 
39398     * @cfg {Number} growMin 
39399     * @hide 
39400     */
39401     /** 
39402     * @cfg {Number} growMax 
39403     * @hide 
39404     */
39405     /**
39406      * @hide
39407      * @method autoSize
39408      */
39409 });/*
39410  * Copyright(c) 2010-2012, Roo J Solutions Limited
39411  *
39412  * Licence LGPL
39413  *
39414  */
39415
39416 /**
39417  * @class Roo.form.ComboBoxArray
39418  * @extends Roo.form.TextField
39419  * A facebook style adder... for lists of email / people / countries  etc...
39420  * pick multiple items from a combo box, and shows each one.
39421  *
39422  *  Fred [x]  Brian [x]  [Pick another |v]
39423  *
39424  *
39425  *  For this to work: it needs various extra information
39426  *    - normal combo problay has
39427  *      name, hiddenName
39428  *    + displayField, valueField
39429  *
39430  *    For our purpose...
39431  *
39432  *
39433  *   If we change from 'extends' to wrapping...
39434  *   
39435  *  
39436  *
39437  
39438  
39439  * @constructor
39440  * Create a new ComboBoxArray.
39441  * @param {Object} config Configuration options
39442  */
39443  
39444
39445 Roo.form.ComboBoxArray = function(config)
39446 {
39447     
39448     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
39449     
39450     this.items = new Roo.util.MixedCollection(false);
39451     
39452     // construct the child combo...
39453     
39454     
39455     
39456     
39457    
39458     
39459 }
39460
39461  
39462 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
39463
39464     /**
39465      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
39466      */
39467     
39468     lastData : false,
39469     
39470     // behavies liek a hiddne field
39471     inputType:      'hidden',
39472     /**
39473      * @cfg {Number} width The width of the box that displays the selected element
39474      */ 
39475     width:          300,
39476
39477     
39478     
39479     /**
39480      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
39481      */
39482     name : false,
39483     /**
39484      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
39485      */
39486     hiddenName : false,
39487     
39488     
39489     // private the array of items that are displayed..
39490     items  : false,
39491     // private - the hidden field el.
39492     hiddenEl : false,
39493     // private - the filed el..
39494     el : false,
39495     
39496     //validateValue : function() { return true; }, // all values are ok!
39497     //onAddClick: function() { },
39498     
39499     onRender : function(ct, position) 
39500     {
39501         
39502         // create the standard hidden element
39503         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
39504         
39505         
39506         // give fake names to child combo;
39507         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
39508         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
39509         
39510         this.combo = Roo.factory(this.combo, Roo.form);
39511         this.combo.onRender(ct, position);
39512         this.combo.initEvents();
39513         
39514         // assigned so form know we need to do this..
39515         this.store          = this.combo.store;
39516         this.valueField     = this.combo.valueField;
39517         this.displayField   = this.combo.displayField ;
39518         
39519         
39520         this.combo.wrap.addClass('x-cbarray-grp');
39521         
39522         var cbwrap = this.combo.wrap.createChild(
39523             {tag: 'div', cls: 'x-cbarray-cb'},
39524             this.combo.el.dom
39525         );
39526         
39527              
39528         this.hiddenEl = this.combo.wrap.createChild({
39529             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
39530         });
39531         this.el = this.combo.wrap.createChild({
39532             tag: 'input',  type:'hidden' , name: this.name, value : ''
39533         });
39534          //   this.el.dom.removeAttribute("name");
39535         
39536         
39537         this.outerWrap = this.combo.wrap;
39538         this.wrap = cbwrap;
39539         
39540         this.outerWrap.setWidth(this.width);
39541         this.outerWrap.dom.removeChild(this.el.dom);
39542         
39543         this.wrap.dom.appendChild(this.el.dom);
39544         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
39545         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
39546         
39547         this.combo.trigger.setStyle('position','relative');
39548         this.combo.trigger.setStyle('left', '0px');
39549         this.combo.trigger.setStyle('top', '2px');
39550         
39551         this.combo.el.setStyle('vertical-align', 'text-bottom');
39552         
39553         //this.trigger.setStyle('vertical-align', 'top');
39554         
39555         // this should use the code from combo really... on('add' ....)
39556         if (this.adder) {
39557             
39558         
39559             this.adder = this.outerWrap.createChild(
39560                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
39561             var _t = this;
39562             this.adder.on('click', function(e) {
39563                 _t.fireEvent('adderclick', this, e);
39564             }, _t);
39565         }
39566         //var _t = this;
39567         //this.adder.on('click', this.onAddClick, _t);
39568         
39569         
39570         this.combo.on('select', function(cb, rec, ix) {
39571             this.addItem(rec.data);
39572             
39573             cb.setValue('');
39574             cb.el.dom.value = '';
39575             //cb.lastData = rec.data;
39576             // add to list
39577             
39578         }, this);
39579         
39580         
39581     },
39582     
39583     
39584     getName: function()
39585     {
39586         // returns hidden if it's set..
39587         if (!this.rendered) {return ''};
39588         return  this.hiddenName ? this.hiddenName : this.name;
39589         
39590     },
39591     
39592     
39593     onResize: function(w, h){
39594         
39595         return;
39596         // not sure if this is needed..
39597         //this.combo.onResize(w,h);
39598         
39599         if(typeof w != 'number'){
39600             // we do not handle it!?!?
39601             return;
39602         }
39603         var tw = this.combo.trigger.getWidth();
39604         tw += this.addicon ? this.addicon.getWidth() : 0;
39605         tw += this.editicon ? this.editicon.getWidth() : 0;
39606         var x = w - tw;
39607         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
39608             
39609         this.combo.trigger.setStyle('left', '0px');
39610         
39611         if(this.list && this.listWidth === undefined){
39612             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
39613             this.list.setWidth(lw);
39614             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39615         }
39616         
39617     
39618         
39619     },
39620     
39621     addItem: function(rec)
39622     {
39623         var valueField = this.combo.valueField;
39624         var displayField = this.combo.displayField;
39625         if (this.items.indexOfKey(rec[valueField]) > -1) {
39626             //console.log("GOT " + rec.data.id);
39627             return;
39628         }
39629         
39630         var x = new Roo.form.ComboBoxArray.Item({
39631             //id : rec[this.idField],
39632             data : rec,
39633             displayField : displayField ,
39634             tipField : displayField ,
39635             cb : this
39636         });
39637         // use the 
39638         this.items.add(rec[valueField],x);
39639         // add it before the element..
39640         this.updateHiddenEl();
39641         x.render(this.outerWrap, this.wrap.dom);
39642         // add the image handler..
39643     },
39644     
39645     updateHiddenEl : function()
39646     {
39647         this.validate();
39648         if (!this.hiddenEl) {
39649             return;
39650         }
39651         var ar = [];
39652         var idField = this.combo.valueField;
39653         
39654         this.items.each(function(f) {
39655             ar.push(f.data[idField]);
39656            
39657         });
39658         this.hiddenEl.dom.value = ar.join(',');
39659         this.validate();
39660     },
39661     
39662     reset : function()
39663     {
39664         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
39665         this.items.each(function(f) {
39666            f.remove(); 
39667         });
39668         this.el.dom.value = '';
39669         if (this.hiddenEl) {
39670             this.hiddenEl.dom.value = '';
39671         }
39672         
39673     },
39674     getValue: function()
39675     {
39676         return this.hiddenEl ? this.hiddenEl.dom.value : '';
39677     },
39678     setValue: function(v) // not a valid action - must use addItems..
39679     {
39680          
39681         this.reset();
39682         
39683         
39684         
39685         if (this.store.isLocal && (typeof(v) == 'string')) {
39686             // then we can use the store to find the values..
39687             // comma seperated at present.. this needs to allow JSON based encoding..
39688             this.hiddenEl.value  = v;
39689             var v_ar = [];
39690             Roo.each(v.split(','), function(k) {
39691                 Roo.log("CHECK " + this.valueField + ',' + k);
39692                 var li = this.store.query(this.valueField, k);
39693                 if (!li.length) {
39694                     return;
39695                 }
39696                 add = {};
39697                 add[this.valueField] = k;
39698                 add[this.displayField] = li.item(0).data[this.displayField];
39699                 
39700                 this.addItem(add);
39701             }, this) 
39702              
39703         }
39704         if (typeof(v) == 'object') {
39705             // then let's assume it's an array of objects..
39706             Roo.each(v, function(l) {
39707                 this.addItem(l);
39708             }, this);
39709              
39710         }
39711         
39712         
39713     },
39714     setFromData: function(v)
39715     {
39716         // this recieves an object, if setValues is called.
39717         this.reset();
39718         this.el.dom.value = v[this.displayField];
39719         this.hiddenEl.dom.value = v[this.valueField];
39720         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
39721             return;
39722         }
39723         var kv = v[this.valueField];
39724         var dv = v[this.displayField];
39725         kv = typeof(kv) != 'string' ? '' : kv;
39726         dv = typeof(dv) != 'string' ? '' : dv;
39727         
39728         
39729         var keys = kv.split(',');
39730         var display = dv.split(',');
39731         for (var i = 0 ; i < keys.length; i++) {
39732             
39733             add = {};
39734             add[this.valueField] = keys[i];
39735             add[this.displayField] = display[i];
39736             this.addItem(add);
39737         }
39738       
39739         
39740     },
39741     
39742     
39743     validateValue : function(value){
39744         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
39745         
39746     }
39747     
39748 });
39749
39750
39751
39752 /**
39753  * @class Roo.form.ComboBoxArray.Item
39754  * @extends Roo.BoxComponent
39755  * A selected item in the list
39756  *  Fred [x]  Brian [x]  [Pick another |v]
39757  * 
39758  * @constructor
39759  * Create a new item.
39760  * @param {Object} config Configuration options
39761  */
39762  
39763 Roo.form.ComboBoxArray.Item = function(config) {
39764     config.id = Roo.id();
39765     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
39766 }
39767
39768 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
39769     data : {},
39770     cb: false,
39771     displayField : false,
39772     tipField : false,
39773     
39774     
39775     defaultAutoCreate : {
39776         tag: 'div',
39777         cls: 'x-cbarray-item',
39778         cn : [ 
39779             { tag: 'div' },
39780             {
39781                 tag: 'img',
39782                 width:16,
39783                 height : 16,
39784                 src : Roo.BLANK_IMAGE_URL ,
39785                 align: 'center'
39786             }
39787         ]
39788         
39789     },
39790     
39791  
39792     onRender : function(ct, position)
39793     {
39794         Roo.form.Field.superclass.onRender.call(this, ct, position);
39795         
39796         if(!this.el){
39797             var cfg = this.getAutoCreate();
39798             this.el = ct.createChild(cfg, position);
39799         }
39800         
39801         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
39802         
39803         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
39804             this.cb.renderer(this.data) :
39805             String.format('{0}',this.data[this.displayField]);
39806         
39807             
39808         this.el.child('div').dom.setAttribute('qtip',
39809                         String.format('{0}',this.data[this.tipField])
39810         );
39811         
39812         this.el.child('img').on('click', this.remove, this);
39813         
39814     },
39815    
39816     remove : function()
39817     {
39818         
39819         this.cb.items.remove(this);
39820         this.el.child('img').un('click', this.remove, this);
39821         this.el.remove();
39822         this.cb.updateHiddenEl();
39823     }
39824     
39825     
39826 });/*
39827  * Based on:
39828  * Ext JS Library 1.1.1
39829  * Copyright(c) 2006-2007, Ext JS, LLC.
39830  *
39831  * Originally Released Under LGPL - original licence link has changed is not relivant.
39832  *
39833  * Fork - LGPL
39834  * <script type="text/javascript">
39835  */
39836 /**
39837  * @class Roo.form.Checkbox
39838  * @extends Roo.form.Field
39839  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
39840  * @constructor
39841  * Creates a new Checkbox
39842  * @param {Object} config Configuration options
39843  */
39844 Roo.form.Checkbox = function(config){
39845     Roo.form.Checkbox.superclass.constructor.call(this, config);
39846     this.addEvents({
39847         /**
39848          * @event check
39849          * Fires when the checkbox is checked or unchecked.
39850              * @param {Roo.form.Checkbox} this This checkbox
39851              * @param {Boolean} checked The new checked value
39852              */
39853         check : true
39854     });
39855 };
39856
39857 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
39858     /**
39859      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
39860      */
39861     focusClass : undefined,
39862     /**
39863      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
39864      */
39865     fieldClass: "x-form-field",
39866     /**
39867      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
39868      */
39869     checked: false,
39870     /**
39871      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39872      * {tag: "input", type: "checkbox", autocomplete: "off"})
39873      */
39874     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
39875     /**
39876      * @cfg {String} boxLabel The text that appears beside the checkbox
39877      */
39878     boxLabel : "",
39879     /**
39880      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
39881      */  
39882     inputValue : '1',
39883     /**
39884      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
39885      */
39886      valueOff: '0', // value when not checked..
39887
39888     actionMode : 'viewEl', 
39889     //
39890     // private
39891     itemCls : 'x-menu-check-item x-form-item',
39892     groupClass : 'x-menu-group-item',
39893     inputType : 'hidden',
39894     
39895     
39896     inSetChecked: false, // check that we are not calling self...
39897     
39898     inputElement: false, // real input element?
39899     basedOn: false, // ????
39900     
39901     isFormField: true, // not sure where this is needed!!!!
39902
39903     onResize : function(){
39904         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
39905         if(!this.boxLabel){
39906             this.el.alignTo(this.wrap, 'c-c');
39907         }
39908     },
39909
39910     initEvents : function(){
39911         Roo.form.Checkbox.superclass.initEvents.call(this);
39912         this.el.on("click", this.onClick,  this);
39913         this.el.on("change", this.onClick,  this);
39914     },
39915
39916
39917     getResizeEl : function(){
39918         return this.wrap;
39919     },
39920
39921     getPositionEl : function(){
39922         return this.wrap;
39923     },
39924
39925     // private
39926     onRender : function(ct, position){
39927         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
39928         /*
39929         if(this.inputValue !== undefined){
39930             this.el.dom.value = this.inputValue;
39931         }
39932         */
39933         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
39934         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
39935         var viewEl = this.wrap.createChild({ 
39936             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
39937         this.viewEl = viewEl;   
39938         this.wrap.on('click', this.onClick,  this); 
39939         
39940         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
39941         this.el.on('propertychange', this.setFromHidden,  this);  //ie
39942         
39943         
39944         
39945         if(this.boxLabel){
39946             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
39947         //    viewEl.on('click', this.onClick,  this); 
39948         }
39949         //if(this.checked){
39950             this.setChecked(this.checked);
39951         //}else{
39952             //this.checked = this.el.dom;
39953         //}
39954
39955     },
39956
39957     // private
39958     initValue : Roo.emptyFn,
39959
39960     /**
39961      * Returns the checked state of the checkbox.
39962      * @return {Boolean} True if checked, else false
39963      */
39964     getValue : function(){
39965         if(this.el){
39966             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
39967         }
39968         return this.valueOff;
39969         
39970     },
39971
39972         // private
39973     onClick : function(){ 
39974         this.setChecked(!this.checked);
39975
39976         //if(this.el.dom.checked != this.checked){
39977         //    this.setValue(this.el.dom.checked);
39978        // }
39979     },
39980
39981     /**
39982      * Sets the checked state of the checkbox.
39983      * On is always based on a string comparison between inputValue and the param.
39984      * @param {Boolean/String} value - the value to set 
39985      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
39986      */
39987     setValue : function(v,suppressEvent){
39988         
39989         
39990         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
39991         //if(this.el && this.el.dom){
39992         //    this.el.dom.checked = this.checked;
39993         //    this.el.dom.defaultChecked = this.checked;
39994         //}
39995         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
39996         //this.fireEvent("check", this, this.checked);
39997     },
39998     // private..
39999     setChecked : function(state,suppressEvent)
40000     {
40001         if (this.inSetChecked) {
40002             this.checked = state;
40003             return;
40004         }
40005         
40006     
40007         if(this.wrap){
40008             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40009         }
40010         this.checked = state;
40011         if(suppressEvent !== true){
40012             this.fireEvent('check', this, state);
40013         }
40014         this.inSetChecked = true;
40015         this.el.dom.value = state ? this.inputValue : this.valueOff;
40016         this.inSetChecked = false;
40017         
40018     },
40019     // handle setting of hidden value by some other method!!?!?
40020     setFromHidden: function()
40021     {
40022         if(!this.el){
40023             return;
40024         }
40025         //console.log("SET FROM HIDDEN");
40026         //alert('setFrom hidden');
40027         this.setValue(this.el.dom.value);
40028     },
40029     
40030     onDestroy : function()
40031     {
40032         if(this.viewEl){
40033             Roo.get(this.viewEl).remove();
40034         }
40035          
40036         Roo.form.Checkbox.superclass.onDestroy.call(this);
40037     }
40038
40039 });/*
40040  * Based on:
40041  * Ext JS Library 1.1.1
40042  * Copyright(c) 2006-2007, Ext JS, LLC.
40043  *
40044  * Originally Released Under LGPL - original licence link has changed is not relivant.
40045  *
40046  * Fork - LGPL
40047  * <script type="text/javascript">
40048  */
40049  
40050 /**
40051  * @class Roo.form.Radio
40052  * @extends Roo.form.Checkbox
40053  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40054  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40055  * @constructor
40056  * Creates a new Radio
40057  * @param {Object} config Configuration options
40058  */
40059 Roo.form.Radio = function(){
40060     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40061 };
40062 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40063     inputType: 'radio',
40064
40065     /**
40066      * If this radio is part of a group, it will return the selected value
40067      * @return {String}
40068      */
40069     getGroupValue : function(){
40070         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40071     }
40072 });//<script type="text/javascript">
40073
40074 /*
40075  * Ext JS Library 1.1.1
40076  * Copyright(c) 2006-2007, Ext JS, LLC.
40077  * licensing@extjs.com
40078  * 
40079  * http://www.extjs.com/license
40080  */
40081  
40082  /*
40083   * 
40084   * Known bugs:
40085   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40086   * - IE ? - no idea how much works there.
40087   * 
40088   * 
40089   * 
40090   */
40091  
40092
40093 /**
40094  * @class Ext.form.HtmlEditor
40095  * @extends Ext.form.Field
40096  * Provides a lightweight HTML Editor component.
40097  *
40098  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40099  * 
40100  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40101  * supported by this editor.</b><br/><br/>
40102  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40103  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40104  */
40105 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40106       /**
40107      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40108      */
40109     toolbars : false,
40110     /**
40111      * @cfg {String} createLinkText The default text for the create link prompt
40112      */
40113     createLinkText : 'Please enter the URL for the link:',
40114     /**
40115      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40116      */
40117     defaultLinkValue : 'http:/'+'/',
40118    
40119      /**
40120      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40121      *                        Roo.resizable.
40122      */
40123     resizable : false,
40124      /**
40125      * @cfg {Number} height (in pixels)
40126      */   
40127     height: 300,
40128    /**
40129      * @cfg {Number} width (in pixels)
40130      */   
40131     width: 500,
40132     
40133     /**
40134      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40135      * 
40136      */
40137     stylesheets: false,
40138     
40139     // id of frame..
40140     frameId: false,
40141     
40142     // private properties
40143     validationEvent : false,
40144     deferHeight: true,
40145     initialized : false,
40146     activated : false,
40147     sourceEditMode : false,
40148     onFocus : Roo.emptyFn,
40149     iframePad:3,
40150     hideMode:'offsets',
40151     
40152     defaultAutoCreate : { // modified by initCompnoent..
40153         tag: "textarea",
40154         style:"width:500px;height:300px;",
40155         autocomplete: "off"
40156     },
40157
40158     // private
40159     initComponent : function(){
40160         this.addEvents({
40161             /**
40162              * @event initialize
40163              * Fires when the editor is fully initialized (including the iframe)
40164              * @param {HtmlEditor} this
40165              */
40166             initialize: true,
40167             /**
40168              * @event activate
40169              * Fires when the editor is first receives the focus. Any insertion must wait
40170              * until after this event.
40171              * @param {HtmlEditor} this
40172              */
40173             activate: true,
40174              /**
40175              * @event beforesync
40176              * Fires before the textarea is updated with content from the editor iframe. Return false
40177              * to cancel the sync.
40178              * @param {HtmlEditor} this
40179              * @param {String} html
40180              */
40181             beforesync: true,
40182              /**
40183              * @event beforepush
40184              * Fires before the iframe editor is updated with content from the textarea. Return false
40185              * to cancel the push.
40186              * @param {HtmlEditor} this
40187              * @param {String} html
40188              */
40189             beforepush: true,
40190              /**
40191              * @event sync
40192              * Fires when the textarea is updated with content from the editor iframe.
40193              * @param {HtmlEditor} this
40194              * @param {String} html
40195              */
40196             sync: true,
40197              /**
40198              * @event push
40199              * Fires when the iframe editor is updated with content from the textarea.
40200              * @param {HtmlEditor} this
40201              * @param {String} html
40202              */
40203             push: true,
40204              /**
40205              * @event editmodechange
40206              * Fires when the editor switches edit modes
40207              * @param {HtmlEditor} this
40208              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40209              */
40210             editmodechange: true,
40211             /**
40212              * @event editorevent
40213              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40214              * @param {HtmlEditor} this
40215              */
40216             editorevent: true
40217         });
40218         this.defaultAutoCreate =  {
40219             tag: "textarea",
40220             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40221             autocomplete: "off"
40222         };
40223     },
40224
40225     /**
40226      * Protected method that will not generally be called directly. It
40227      * is called when the editor creates its toolbar. Override this method if you need to
40228      * add custom toolbar buttons.
40229      * @param {HtmlEditor} editor
40230      */
40231     createToolbar : function(editor){
40232         if (!editor.toolbars || !editor.toolbars.length) {
40233             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40234         }
40235         
40236         for (var i =0 ; i < editor.toolbars.length;i++) {
40237             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
40238             editor.toolbars[i].init(editor);
40239         }
40240          
40241         
40242     },
40243
40244     /**
40245      * Protected method that will not generally be called directly. It
40246      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40247      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40248      */
40249     getDocMarkup : function(){
40250         // body styles..
40251         var st = '';
40252         if (this.stylesheets === false) {
40253             
40254             Roo.get(document.head).select('style').each(function(node) {
40255                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40256             });
40257             
40258             Roo.get(document.head).select('link').each(function(node) { 
40259                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40260             });
40261             
40262         } else if (!this.stylesheets.length) {
40263                 // simple..
40264                 st = '<style type="text/css">' +
40265                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40266                    '</style>';
40267         } else {
40268             Roo.each(this.stylesheets, function(s) {
40269                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
40270             });
40271             
40272         }
40273         
40274         st +=  '<style type="text/css">' +
40275             'IMG { cursor: pointer } ' +
40276         '</style>';
40277
40278         
40279         return '<html><head>' + st  +
40280             //<style type="text/css">' +
40281             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40282             //'</style>' +
40283             ' </head><body class="roo-htmleditor-body"></body></html>';
40284     },
40285
40286     // private
40287     onRender : function(ct, position)
40288     {
40289         var _t = this;
40290         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
40291         this.el.dom.style.border = '0 none';
40292         this.el.dom.setAttribute('tabIndex', -1);
40293         this.el.addClass('x-hidden');
40294         if(Roo.isIE){ // fix IE 1px bogus margin
40295             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
40296         }
40297         this.wrap = this.el.wrap({
40298             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
40299         });
40300         
40301         if (this.resizable) {
40302             this.resizeEl = new Roo.Resizable(this.wrap, {
40303                 pinned : true,
40304                 wrap: true,
40305                 dynamic : true,
40306                 minHeight : this.height,
40307                 height: this.height,
40308                 handles : this.resizable,
40309                 width: this.width,
40310                 listeners : {
40311                     resize : function(r, w, h) {
40312                         _t.onResize(w,h); // -something
40313                     }
40314                 }
40315             });
40316             
40317         }
40318
40319         this.frameId = Roo.id();
40320         
40321         this.createToolbar(this);
40322         
40323       
40324         
40325         var iframe = this.wrap.createChild({
40326             tag: 'iframe',
40327             id: this.frameId,
40328             name: this.frameId,
40329             frameBorder : 'no',
40330             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
40331         }, this.el
40332         );
40333         
40334        // console.log(iframe);
40335         //this.wrap.dom.appendChild(iframe);
40336
40337         this.iframe = iframe.dom;
40338
40339          this.assignDocWin();
40340         
40341         this.doc.designMode = 'on';
40342        
40343         this.doc.open();
40344         this.doc.write(this.getDocMarkup());
40345         this.doc.close();
40346
40347         
40348         var task = { // must defer to wait for browser to be ready
40349             run : function(){
40350                 //console.log("run task?" + this.doc.readyState);
40351                 this.assignDocWin();
40352                 if(this.doc.body || this.doc.readyState == 'complete'){
40353                     try {
40354                         this.doc.designMode="on";
40355                     } catch (e) {
40356                         return;
40357                     }
40358                     Roo.TaskMgr.stop(task);
40359                     this.initEditor.defer(10, this);
40360                 }
40361             },
40362             interval : 10,
40363             duration:10000,
40364             scope: this
40365         };
40366         Roo.TaskMgr.start(task);
40367
40368         if(!this.width){
40369             this.setSize(this.wrap.getSize());
40370         }
40371         if (this.resizeEl) {
40372             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
40373             // should trigger onReize..
40374         }
40375     },
40376
40377     // private
40378     onResize : function(w, h)
40379     {
40380         //Roo.log('resize: ' +w + ',' + h );
40381         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
40382         if(this.el && this.iframe){
40383             if(typeof w == 'number'){
40384                 var aw = w - this.wrap.getFrameWidth('lr');
40385                 this.el.setWidth(this.adjustWidth('textarea', aw));
40386                 this.iframe.style.width = aw + 'px';
40387             }
40388             if(typeof h == 'number'){
40389                 var tbh = 0;
40390                 for (var i =0; i < this.toolbars.length;i++) {
40391                     // fixme - ask toolbars for heights?
40392                     tbh += this.toolbars[i].tb.el.getHeight();
40393                     if (this.toolbars[i].footer) {
40394                         tbh += this.toolbars[i].footer.el.getHeight();
40395                     }
40396                 }
40397                 
40398                 
40399                 
40400                 
40401                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
40402                 ah -= 5; // knock a few pixes off for look..
40403                 this.el.setHeight(this.adjustWidth('textarea', ah));
40404                 this.iframe.style.height = ah + 'px';
40405                 if(this.doc){
40406                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
40407                 }
40408             }
40409         }
40410     },
40411
40412     /**
40413      * Toggles the editor between standard and source edit mode.
40414      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
40415      */
40416     toggleSourceEdit : function(sourceEditMode){
40417         
40418         this.sourceEditMode = sourceEditMode === true;
40419         
40420         if(this.sourceEditMode){
40421           
40422             this.syncValue();
40423             this.iframe.className = 'x-hidden';
40424             this.el.removeClass('x-hidden');
40425             this.el.dom.removeAttribute('tabIndex');
40426             this.el.focus();
40427         }else{
40428              
40429             this.pushValue();
40430             this.iframe.className = '';
40431             this.el.addClass('x-hidden');
40432             this.el.dom.setAttribute('tabIndex', -1);
40433             this.deferFocus();
40434         }
40435         this.setSize(this.wrap.getSize());
40436         this.fireEvent('editmodechange', this, this.sourceEditMode);
40437     },
40438
40439     // private used internally
40440     createLink : function(){
40441         var url = prompt(this.createLinkText, this.defaultLinkValue);
40442         if(url && url != 'http:/'+'/'){
40443             this.relayCmd('createlink', url);
40444         }
40445     },
40446
40447     // private (for BoxComponent)
40448     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40449
40450     // private (for BoxComponent)
40451     getResizeEl : function(){
40452         return this.wrap;
40453     },
40454
40455     // private (for BoxComponent)
40456     getPositionEl : function(){
40457         return this.wrap;
40458     },
40459
40460     // private
40461     initEvents : function(){
40462         this.originalValue = this.getValue();
40463     },
40464
40465     /**
40466      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40467      * @method
40468      */
40469     markInvalid : Roo.emptyFn,
40470     /**
40471      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40472      * @method
40473      */
40474     clearInvalid : Roo.emptyFn,
40475
40476     setValue : function(v){
40477         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
40478         this.pushValue();
40479     },
40480
40481     /**
40482      * Protected method that will not generally be called directly. If you need/want
40483      * custom HTML cleanup, this is the method you should override.
40484      * @param {String} html The HTML to be cleaned
40485      * return {String} The cleaned HTML
40486      */
40487     cleanHtml : function(html){
40488         html = String(html);
40489         if(html.length > 5){
40490             if(Roo.isSafari){ // strip safari nonsense
40491                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
40492             }
40493         }
40494         if(html == '&nbsp;'){
40495             html = '';
40496         }
40497         return html;
40498     },
40499
40500     /**
40501      * Protected method that will not generally be called directly. Syncs the contents
40502      * of the editor iframe with the textarea.
40503      */
40504     syncValue : function(){
40505         if(this.initialized){
40506             var bd = (this.doc.body || this.doc.documentElement);
40507             //this.cleanUpPaste(); -- this is done else where and causes havoc..
40508             var html = bd.innerHTML;
40509             if(Roo.isSafari){
40510                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
40511                 var m = bs.match(/text-align:(.*?);/i);
40512                 if(m && m[1]){
40513                     html = '<div style="'+m[0]+'">' + html + '</div>';
40514                 }
40515             }
40516             html = this.cleanHtml(html);
40517             // fix up the special chars..
40518             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
40519                 return "&#"+b.charCodeAt()+";" 
40520             });
40521             if(this.fireEvent('beforesync', this, html) !== false){
40522                 this.el.dom.value = html;
40523                 this.fireEvent('sync', this, html);
40524             }
40525         }
40526     },
40527
40528     /**
40529      * Protected method that will not generally be called directly. Pushes the value of the textarea
40530      * into the iframe editor.
40531      */
40532     pushValue : function(){
40533         if(this.initialized){
40534             var v = this.el.dom.value;
40535             if(v.length < 1){
40536                 v = '&#160;';
40537             }
40538             
40539             if(this.fireEvent('beforepush', this, v) !== false){
40540                 var d = (this.doc.body || this.doc.documentElement);
40541                 d.innerHTML = v;
40542                 this.cleanUpPaste();
40543                 this.el.dom.value = d.innerHTML;
40544                 this.fireEvent('push', this, v);
40545             }
40546         }
40547     },
40548
40549     // private
40550     deferFocus : function(){
40551         this.focus.defer(10, this);
40552     },
40553
40554     // doc'ed in Field
40555     focus : function(){
40556         if(this.win && !this.sourceEditMode){
40557             this.win.focus();
40558         }else{
40559             this.el.focus();
40560         }
40561     },
40562     
40563     assignDocWin: function()
40564     {
40565         var iframe = this.iframe;
40566         
40567          if(Roo.isIE){
40568             this.doc = iframe.contentWindow.document;
40569             this.win = iframe.contentWindow;
40570         } else {
40571             if (!Roo.get(this.frameId)) {
40572                 return;
40573             }
40574             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
40575             this.win = Roo.get(this.frameId).dom.contentWindow;
40576         }
40577     },
40578     
40579     // private
40580     initEditor : function(){
40581         //console.log("INIT EDITOR");
40582         this.assignDocWin();
40583         
40584         
40585         
40586         this.doc.designMode="on";
40587         this.doc.open();
40588         this.doc.write(this.getDocMarkup());
40589         this.doc.close();
40590         
40591         var dbody = (this.doc.body || this.doc.documentElement);
40592         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
40593         // this copies styles from the containing element into thsi one..
40594         // not sure why we need all of this..
40595         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
40596         ss['background-attachment'] = 'fixed'; // w3c
40597         dbody.bgProperties = 'fixed'; // ie
40598         Roo.DomHelper.applyStyles(dbody, ss);
40599         Roo.EventManager.on(this.doc, {
40600             //'mousedown': this.onEditorEvent,
40601             'mouseup': this.onEditorEvent,
40602             'dblclick': this.onEditorEvent,
40603             'click': this.onEditorEvent,
40604             'keyup': this.onEditorEvent,
40605             buffer:100,
40606             scope: this
40607         });
40608         if(Roo.isGecko){
40609             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
40610         }
40611         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
40612             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
40613         }
40614         this.initialized = true;
40615
40616         this.fireEvent('initialize', this);
40617         this.pushValue();
40618     },
40619
40620     // private
40621     onDestroy : function(){
40622         
40623         
40624         
40625         if(this.rendered){
40626             
40627             for (var i =0; i < this.toolbars.length;i++) {
40628                 // fixme - ask toolbars for heights?
40629                 this.toolbars[i].onDestroy();
40630             }
40631             
40632             this.wrap.dom.innerHTML = '';
40633             this.wrap.remove();
40634         }
40635     },
40636
40637     // private
40638     onFirstFocus : function(){
40639         
40640         this.assignDocWin();
40641         
40642         
40643         this.activated = true;
40644         for (var i =0; i < this.toolbars.length;i++) {
40645             this.toolbars[i].onFirstFocus();
40646         }
40647        
40648         if(Roo.isGecko){ // prevent silly gecko errors
40649             this.win.focus();
40650             var s = this.win.getSelection();
40651             if(!s.focusNode || s.focusNode.nodeType != 3){
40652                 var r = s.getRangeAt(0);
40653                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
40654                 r.collapse(true);
40655                 this.deferFocus();
40656             }
40657             try{
40658                 this.execCmd('useCSS', true);
40659                 this.execCmd('styleWithCSS', false);
40660             }catch(e){}
40661         }
40662         this.fireEvent('activate', this);
40663     },
40664
40665     // private
40666     adjustFont: function(btn){
40667         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
40668         //if(Roo.isSafari){ // safari
40669         //    adjust *= 2;
40670        // }
40671         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
40672         if(Roo.isSafari){ // safari
40673             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
40674             v =  (v < 10) ? 10 : v;
40675             v =  (v > 48) ? 48 : v;
40676             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
40677             
40678         }
40679         
40680         
40681         v = Math.max(1, v+adjust);
40682         
40683         this.execCmd('FontSize', v  );
40684     },
40685
40686     onEditorEvent : function(e){
40687         this.fireEvent('editorevent', this, e);
40688       //  this.updateToolbar();
40689         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
40690     },
40691
40692     insertTag : function(tg)
40693     {
40694         // could be a bit smarter... -> wrap the current selected tRoo..
40695         
40696         this.execCmd("formatblock",   tg);
40697         
40698     },
40699     
40700     insertText : function(txt)
40701     {
40702         
40703         
40704         range = this.createRange();
40705         range.deleteContents();
40706                //alert(Sender.getAttribute('label'));
40707                
40708         range.insertNode(this.doc.createTextNode(txt));
40709     } ,
40710     
40711     // private
40712     relayBtnCmd : function(btn){
40713         this.relayCmd(btn.cmd);
40714     },
40715
40716     /**
40717      * Executes a Midas editor command on the editor document and performs necessary focus and
40718      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
40719      * @param {String} cmd The Midas command
40720      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40721      */
40722     relayCmd : function(cmd, value){
40723         this.win.focus();
40724         this.execCmd(cmd, value);
40725         this.fireEvent('editorevent', this);
40726         //this.updateToolbar();
40727         this.deferFocus();
40728     },
40729
40730     /**
40731      * Executes a Midas editor command directly on the editor document.
40732      * For visual commands, you should use {@link #relayCmd} instead.
40733      * <b>This should only be called after the editor is initialized.</b>
40734      * @param {String} cmd The Midas command
40735      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40736      */
40737     execCmd : function(cmd, value){
40738         this.doc.execCommand(cmd, false, value === undefined ? null : value);
40739         this.syncValue();
40740     },
40741  
40742  
40743    
40744     /**
40745      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
40746      * to insert tRoo.
40747      * @param {String} text | dom node.. 
40748      */
40749     insertAtCursor : function(text)
40750     {
40751         
40752         
40753         
40754         if(!this.activated){
40755             return;
40756         }
40757         /*
40758         if(Roo.isIE){
40759             this.win.focus();
40760             var r = this.doc.selection.createRange();
40761             if(r){
40762                 r.collapse(true);
40763                 r.pasteHTML(text);
40764                 this.syncValue();
40765                 this.deferFocus();
40766             
40767             }
40768             return;
40769         }
40770         */
40771         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
40772             this.win.focus();
40773             
40774             
40775             // from jquery ui (MIT licenced)
40776             var range, node;
40777             var win = this.win;
40778             
40779             if (win.getSelection && win.getSelection().getRangeAt) {
40780                 range = win.getSelection().getRangeAt(0);
40781                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
40782                 range.insertNode(node);
40783             } else if (win.document.selection && win.document.selection.createRange) {
40784                 // no firefox support
40785                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40786                 win.document.selection.createRange().pasteHTML(txt);
40787             } else {
40788                 // no firefox support
40789                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40790                 this.execCmd('InsertHTML', txt);
40791             } 
40792             
40793             this.syncValue();
40794             
40795             this.deferFocus();
40796         }
40797     },
40798  // private
40799     mozKeyPress : function(e){
40800         if(e.ctrlKey){
40801             var c = e.getCharCode(), cmd;
40802           
40803             if(c > 0){
40804                 c = String.fromCharCode(c).toLowerCase();
40805                 switch(c){
40806                     case 'b':
40807                         cmd = 'bold';
40808                         break;
40809                     case 'i':
40810                         cmd = 'italic';
40811                         break;
40812                     
40813                     case 'u':
40814                         cmd = 'underline';
40815                         break;
40816                     
40817                     case 'v':
40818                         this.cleanUpPaste.defer(100, this);
40819                         return;
40820                         
40821                 }
40822                 if(cmd){
40823                     this.win.focus();
40824                     this.execCmd(cmd);
40825                     this.deferFocus();
40826                     e.preventDefault();
40827                 }
40828                 
40829             }
40830         }
40831     },
40832
40833     // private
40834     fixKeys : function(){ // load time branching for fastest keydown performance
40835         if(Roo.isIE){
40836             return function(e){
40837                 var k = e.getKey(), r;
40838                 if(k == e.TAB){
40839                     e.stopEvent();
40840                     r = this.doc.selection.createRange();
40841                     if(r){
40842                         r.collapse(true);
40843                         r.pasteHTML('&#160;&#160;&#160;&#160;');
40844                         this.deferFocus();
40845                     }
40846                     return;
40847                 }
40848                 
40849                 if(k == e.ENTER){
40850                     r = this.doc.selection.createRange();
40851                     if(r){
40852                         var target = r.parentElement();
40853                         if(!target || target.tagName.toLowerCase() != 'li'){
40854                             e.stopEvent();
40855                             r.pasteHTML('<br />');
40856                             r.collapse(false);
40857                             r.select();
40858                         }
40859                     }
40860                 }
40861                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40862                     this.cleanUpPaste.defer(100, this);
40863                     return;
40864                 }
40865                 
40866                 
40867             };
40868         }else if(Roo.isOpera){
40869             return function(e){
40870                 var k = e.getKey();
40871                 if(k == e.TAB){
40872                     e.stopEvent();
40873                     this.win.focus();
40874                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
40875                     this.deferFocus();
40876                 }
40877                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40878                     this.cleanUpPaste.defer(100, this);
40879                     return;
40880                 }
40881                 
40882             };
40883         }else if(Roo.isSafari){
40884             return function(e){
40885                 var k = e.getKey();
40886                 
40887                 if(k == e.TAB){
40888                     e.stopEvent();
40889                     this.execCmd('InsertText','\t');
40890                     this.deferFocus();
40891                     return;
40892                 }
40893                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40894                     this.cleanUpPaste.defer(100, this);
40895                     return;
40896                 }
40897                 
40898              };
40899         }
40900     }(),
40901     
40902     getAllAncestors: function()
40903     {
40904         var p = this.getSelectedNode();
40905         var a = [];
40906         if (!p) {
40907             a.push(p); // push blank onto stack..
40908             p = this.getParentElement();
40909         }
40910         
40911         
40912         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
40913             a.push(p);
40914             p = p.parentNode;
40915         }
40916         a.push(this.doc.body);
40917         return a;
40918     },
40919     lastSel : false,
40920     lastSelNode : false,
40921     
40922     
40923     getSelection : function() 
40924     {
40925         this.assignDocWin();
40926         return Roo.isIE ? this.doc.selection : this.win.getSelection();
40927     },
40928     
40929     getSelectedNode: function() 
40930     {
40931         // this may only work on Gecko!!!
40932         
40933         // should we cache this!!!!
40934         
40935         
40936         
40937          
40938         var range = this.createRange(this.getSelection()).cloneRange();
40939         
40940         if (Roo.isIE) {
40941             var parent = range.parentElement();
40942             while (true) {
40943                 var testRange = range.duplicate();
40944                 testRange.moveToElementText(parent);
40945                 if (testRange.inRange(range)) {
40946                     break;
40947                 }
40948                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
40949                     break;
40950                 }
40951                 parent = parent.parentElement;
40952             }
40953             return parent;
40954         }
40955         
40956         // is ancestor a text element.
40957         var ac =  range.commonAncestorContainer;
40958         if (ac.nodeType == 3) {
40959             ac = ac.parentNode;
40960         }
40961         
40962         var ar = ac.childNodes;
40963          
40964         var nodes = [];
40965         var other_nodes = [];
40966         var has_other_nodes = false;
40967         for (var i=0;i<ar.length;i++) {
40968             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
40969                 continue;
40970             }
40971             // fullly contained node.
40972             
40973             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
40974                 nodes.push(ar[i]);
40975                 continue;
40976             }
40977             
40978             // probably selected..
40979             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
40980                 other_nodes.push(ar[i]);
40981                 continue;
40982             }
40983             // outer..
40984             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
40985                 continue;
40986             }
40987             
40988             
40989             has_other_nodes = true;
40990         }
40991         if (!nodes.length && other_nodes.length) {
40992             nodes= other_nodes;
40993         }
40994         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
40995             return false;
40996         }
40997         
40998         return nodes[0];
40999     },
41000     createRange: function(sel)
41001     {
41002         // this has strange effects when using with 
41003         // top toolbar - not sure if it's a great idea.
41004         //this.editor.contentWindow.focus();
41005         if (typeof sel != "undefined") {
41006             try {
41007                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41008             } catch(e) {
41009                 return this.doc.createRange();
41010             }
41011         } else {
41012             return this.doc.createRange();
41013         }
41014     },
41015     getParentElement: function()
41016     {
41017         
41018         this.assignDocWin();
41019         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41020         
41021         var range = this.createRange(sel);
41022          
41023         try {
41024             var p = range.commonAncestorContainer;
41025             while (p.nodeType == 3) { // text node
41026                 p = p.parentNode;
41027             }
41028             return p;
41029         } catch (e) {
41030             return null;
41031         }
41032     
41033     },
41034     /***
41035      *
41036      * Range intersection.. the hard stuff...
41037      *  '-1' = before
41038      *  '0' = hits..
41039      *  '1' = after.
41040      *         [ -- selected range --- ]
41041      *   [fail]                        [fail]
41042      *
41043      *    basically..
41044      *      if end is before start or  hits it. fail.
41045      *      if start is after end or hits it fail.
41046      *
41047      *   if either hits (but other is outside. - then it's not 
41048      *   
41049      *    
41050      **/
41051     
41052     
41053     // @see http://www.thismuchiknow.co.uk/?p=64.
41054     rangeIntersectsNode : function(range, node)
41055     {
41056         var nodeRange = node.ownerDocument.createRange();
41057         try {
41058             nodeRange.selectNode(node);
41059         } catch (e) {
41060             nodeRange.selectNodeContents(node);
41061         }
41062     
41063         var rangeStartRange = range.cloneRange();
41064         rangeStartRange.collapse(true);
41065     
41066         var rangeEndRange = range.cloneRange();
41067         rangeEndRange.collapse(false);
41068     
41069         var nodeStartRange = nodeRange.cloneRange();
41070         nodeStartRange.collapse(true);
41071     
41072         var nodeEndRange = nodeRange.cloneRange();
41073         nodeEndRange.collapse(false);
41074     
41075         return rangeStartRange.compareBoundaryPoints(
41076                  Range.START_TO_START, nodeEndRange) == -1 &&
41077                rangeEndRange.compareBoundaryPoints(
41078                  Range.START_TO_START, nodeStartRange) == 1;
41079         
41080          
41081     },
41082     rangeCompareNode : function(range, node)
41083     {
41084         var nodeRange = node.ownerDocument.createRange();
41085         try {
41086             nodeRange.selectNode(node);
41087         } catch (e) {
41088             nodeRange.selectNodeContents(node);
41089         }
41090         
41091         
41092         range.collapse(true);
41093     
41094         nodeRange.collapse(true);
41095      
41096         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41097         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41098          
41099         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41100         
41101         var nodeIsBefore   =  ss == 1;
41102         var nodeIsAfter    = ee == -1;
41103         
41104         if (nodeIsBefore && nodeIsAfter)
41105             return 0; // outer
41106         if (!nodeIsBefore && nodeIsAfter)
41107             return 1; //right trailed.
41108         
41109         if (nodeIsBefore && !nodeIsAfter)
41110             return 2;  // left trailed.
41111         // fully contined.
41112         return 3;
41113     },
41114
41115     // private? - in a new class?
41116     cleanUpPaste :  function()
41117     {
41118         // cleans up the whole document..
41119          Roo.log('cleanuppaste');
41120         this.cleanUpChildren(this.doc.body);
41121         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41122         if (clean != this.doc.body.innerHTML) {
41123             this.doc.body.innerHTML = clean;
41124         }
41125         
41126     },
41127     
41128     cleanWordChars : function(input) {
41129         var he = Roo.form.HtmlEditor;
41130     
41131         var output = input;
41132         Roo.each(he.swapCodes, function(sw) { 
41133         
41134             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41135             output = output.replace(swapper, sw[1]);
41136         });
41137         return output;
41138     },
41139     
41140     
41141     cleanUpChildren : function (n)
41142     {
41143         if (!n.childNodes.length) {
41144             return;
41145         }
41146         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41147            this.cleanUpChild(n.childNodes[i]);
41148         }
41149     },
41150     
41151     
41152         
41153     
41154     cleanUpChild : function (node)
41155     {
41156         //console.log(node);
41157         if (node.nodeName == "#text") {
41158             // clean up silly Windows -- stuff?
41159             return; 
41160         }
41161         if (node.nodeName == "#comment") {
41162             node.parentNode.removeChild(node);
41163             // clean up silly Windows -- stuff?
41164             return; 
41165         }
41166         
41167         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41168             // remove node.
41169             node.parentNode.removeChild(node);
41170             return;
41171             
41172         }
41173         
41174         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41175         
41176         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41177         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41178         
41179         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41180         //    remove_keep_children = true;
41181         //}
41182         
41183         if (remove_keep_children) {
41184             this.cleanUpChildren(node);
41185             // inserts everything just before this node...
41186             while (node.childNodes.length) {
41187                 var cn = node.childNodes[0];
41188                 node.removeChild(cn);
41189                 node.parentNode.insertBefore(cn, node);
41190             }
41191             node.parentNode.removeChild(node);
41192             return;
41193         }
41194         
41195         if (!node.attributes || !node.attributes.length) {
41196             this.cleanUpChildren(node);
41197             return;
41198         }
41199         
41200         function cleanAttr(n,v)
41201         {
41202             
41203             if (v.match(/^\./) || v.match(/^\//)) {
41204                 return;
41205             }
41206             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41207                 return;
41208             }
41209             if (v.match(/^#/)) {
41210                 return;
41211             }
41212             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
41213             node.removeAttribute(n);
41214             
41215         }
41216         
41217         function cleanStyle(n,v)
41218         {
41219             if (v.match(/expression/)) { //XSS?? should we even bother..
41220                 node.removeAttribute(n);
41221                 return;
41222             }
41223             
41224             
41225             var parts = v.split(/;/);
41226             Roo.each(parts, function(p) {
41227                 p = p.replace(/\s+/g,'');
41228                 if (!p.length) {
41229                     return true;
41230                 }
41231                 var l = p.split(':').shift().replace(/\s+/g,'');
41232                 
41233                 // only allow 'c whitelisted system attributes'
41234                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
41235                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
41236                     node.removeAttribute(n);
41237                     return false;
41238                 }
41239                 return true;
41240             });
41241             
41242             
41243         }
41244         
41245         
41246         for (var i = node.attributes.length-1; i > -1 ; i--) {
41247             var a = node.attributes[i];
41248             //console.log(a);
41249             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
41250                 node.removeAttribute(a.name);
41251                 continue;
41252             }
41253             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
41254                 cleanAttr(a.name,a.value); // fixme..
41255                 continue;
41256             }
41257             if (a.name == 'style') {
41258                 cleanStyle(a.name,a.value);
41259                 continue;
41260             }
41261             /// clean up MS crap..
41262             // tecnically this should be a list of valid class'es..
41263             
41264             
41265             if (a.name == 'class') {
41266                 if (a.value.match(/^Mso/)) {
41267                     node.className = '';
41268                 }
41269                 
41270                 if (a.value.match(/body/)) {
41271                     node.className = '';
41272                 }
41273                 continue;
41274             }
41275             
41276             // style cleanup!?
41277             // class cleanup?
41278             
41279         }
41280         
41281         
41282         this.cleanUpChildren(node);
41283         
41284         
41285     }
41286     
41287     
41288     // hide stuff that is not compatible
41289     /**
41290      * @event blur
41291      * @hide
41292      */
41293     /**
41294      * @event change
41295      * @hide
41296      */
41297     /**
41298      * @event focus
41299      * @hide
41300      */
41301     /**
41302      * @event specialkey
41303      * @hide
41304      */
41305     /**
41306      * @cfg {String} fieldClass @hide
41307      */
41308     /**
41309      * @cfg {String} focusClass @hide
41310      */
41311     /**
41312      * @cfg {String} autoCreate @hide
41313      */
41314     /**
41315      * @cfg {String} inputType @hide
41316      */
41317     /**
41318      * @cfg {String} invalidClass @hide
41319      */
41320     /**
41321      * @cfg {String} invalidText @hide
41322      */
41323     /**
41324      * @cfg {String} msgFx @hide
41325      */
41326     /**
41327      * @cfg {String} validateOnBlur @hide
41328      */
41329 });
41330
41331 Roo.form.HtmlEditor.white = [
41332         'area', 'br', 'img', 'input', 'hr', 'wbr',
41333         
41334        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
41335        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
41336        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
41337        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
41338        'table',   'ul',         'xmp', 
41339        
41340        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
41341       'thead',   'tr', 
41342      
41343       'dir', 'menu', 'ol', 'ul', 'dl',
41344        
41345       'embed',  'object'
41346 ];
41347
41348
41349 Roo.form.HtmlEditor.black = [
41350     //    'embed',  'object', // enable - backend responsiblity to clean thiese
41351         'applet', // 
41352         'base',   'basefont', 'bgsound', 'blink',  'body', 
41353         'frame',  'frameset', 'head',    'html',   'ilayer', 
41354         'iframe', 'layer',  'link',     'meta',    'object',   
41355         'script', 'style' ,'title',  'xml' // clean later..
41356 ];
41357 Roo.form.HtmlEditor.clean = [
41358     'script', 'style', 'title', 'xml'
41359 ];
41360 Roo.form.HtmlEditor.remove = [
41361     'font'
41362 ];
41363 // attributes..
41364
41365 Roo.form.HtmlEditor.ablack = [
41366     'on'
41367 ];
41368     
41369 Roo.form.HtmlEditor.aclean = [ 
41370     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
41371 ];
41372
41373 // protocols..
41374 Roo.form.HtmlEditor.pwhite= [
41375         'http',  'https',  'mailto'
41376 ];
41377
41378 // white listed style attributes.
41379 Roo.form.HtmlEditor.cwhite= [
41380         'text-align',
41381         'font-size'
41382 ];
41383
41384
41385 Roo.form.HtmlEditor.swapCodes   =[ 
41386     [    8211, "--" ], 
41387     [    8212, "--" ], 
41388     [    8216,  "'" ],  
41389     [    8217, "'" ],  
41390     [    8220, '"' ],  
41391     [    8221, '"' ],  
41392     [    8226, "*" ],  
41393     [    8230, "..." ]
41394 ]; 
41395
41396     // <script type="text/javascript">
41397 /*
41398  * Based on
41399  * Ext JS Library 1.1.1
41400  * Copyright(c) 2006-2007, Ext JS, LLC.
41401  *  
41402  
41403  */
41404
41405 /**
41406  * @class Roo.form.HtmlEditorToolbar1
41407  * Basic Toolbar
41408  * 
41409  * Usage:
41410  *
41411  new Roo.form.HtmlEditor({
41412     ....
41413     toolbars : [
41414         new Roo.form.HtmlEditorToolbar1({
41415             disable : { fonts: 1 , format: 1, ..., ... , ...],
41416             btns : [ .... ]
41417         })
41418     }
41419      
41420  * 
41421  * @cfg {Object} disable List of elements to disable..
41422  * @cfg {Array} btns List of additional buttons.
41423  * 
41424  * 
41425  * NEEDS Extra CSS? 
41426  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
41427  */
41428  
41429 Roo.form.HtmlEditor.ToolbarStandard = function(config)
41430 {
41431     
41432     Roo.apply(this, config);
41433     
41434     // default disabled, based on 'good practice'..
41435     this.disable = this.disable || {};
41436     Roo.applyIf(this.disable, {
41437         fontSize : true,
41438         colors : true,
41439         specialElements : true
41440     });
41441     
41442     
41443     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
41444     // dont call parent... till later.
41445 }
41446
41447 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
41448     
41449     tb: false,
41450     
41451     rendered: false,
41452     
41453     editor : false,
41454     /**
41455      * @cfg {Object} disable  List of toolbar elements to disable
41456          
41457      */
41458     disable : false,
41459       /**
41460      * @cfg {Array} fontFamilies An array of available font families
41461      */
41462     fontFamilies : [
41463         'Arial',
41464         'Courier New',
41465         'Tahoma',
41466         'Times New Roman',
41467         'Verdana'
41468     ],
41469     
41470     specialChars : [
41471            "&#169;",
41472           "&#174;",     
41473           "&#8482;",    
41474           "&#163;" ,    
41475          // "&#8212;",    
41476           "&#8230;",    
41477           "&#247;" ,    
41478         //  "&#225;" ,     ?? a acute?
41479            "&#8364;"    , //Euro
41480        //   "&#8220;"    ,
41481         //  "&#8221;"    ,
41482         //  "&#8226;"    ,
41483           "&#176;"  //   , // degrees
41484
41485          // "&#233;"     , // e ecute
41486          // "&#250;"     , // u ecute?
41487     ],
41488     
41489     specialElements : [
41490         {
41491             text: "Insert Table",
41492             xtype: 'MenuItem',
41493             xns : Roo.Menu,
41494             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
41495                 
41496         },
41497         {    
41498             text: "Insert Image",
41499             xtype: 'MenuItem',
41500             xns : Roo.Menu,
41501             ihtml : '<img src="about:blank"/>'
41502             
41503         }
41504         
41505          
41506     ],
41507     
41508     
41509     inputElements : [ 
41510             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
41511             "input:submit", "input:button", "select", "textarea", "label" ],
41512     formats : [
41513         ["p"] ,  
41514         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
41515         ["pre"],[ "code"], 
41516         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
41517     ],
41518      /**
41519      * @cfg {String} defaultFont default font to use.
41520      */
41521     defaultFont: 'tahoma',
41522    
41523     fontSelect : false,
41524     
41525     
41526     formatCombo : false,
41527     
41528     init : function(editor)
41529     {
41530         this.editor = editor;
41531         
41532         
41533         var fid = editor.frameId;
41534         var etb = this;
41535         function btn(id, toggle, handler){
41536             var xid = fid + '-'+ id ;
41537             return {
41538                 id : xid,
41539                 cmd : id,
41540                 cls : 'x-btn-icon x-edit-'+id,
41541                 enableToggle:toggle !== false,
41542                 scope: editor, // was editor...
41543                 handler:handler||editor.relayBtnCmd,
41544                 clickEvent:'mousedown',
41545                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
41546                 tabIndex:-1
41547             };
41548         }
41549         
41550         
41551         
41552         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
41553         this.tb = tb;
41554          // stop form submits
41555         tb.el.on('click', function(e){
41556             e.preventDefault(); // what does this do?
41557         });
41558
41559         if(!this.disable.font && !Roo.isSafari){
41560             /* why no safari for fonts
41561             editor.fontSelect = tb.el.createChild({
41562                 tag:'select',
41563                 tabIndex: -1,
41564                 cls:'x-font-select',
41565                 html: editor.createFontOptions()
41566             });
41567             editor.fontSelect.on('change', function(){
41568                 var font = editor.fontSelect.dom.value;
41569                 editor.relayCmd('fontname', font);
41570                 editor.deferFocus();
41571             }, editor);
41572             tb.add(
41573                 editor.fontSelect.dom,
41574                 '-'
41575             );
41576             */
41577         };
41578         if(!this.disable.formats){
41579             this.formatCombo = new Roo.form.ComboBox({
41580                 store: new Roo.data.SimpleStore({
41581                     id : 'tag',
41582                     fields: ['tag'],
41583                     data : this.formats // from states.js
41584                 }),
41585                 blockFocus : true,
41586                 //autoCreate : {tag: "div",  size: "20"},
41587                 displayField:'tag',
41588                 typeAhead: false,
41589                 mode: 'local',
41590                 editable : false,
41591                 triggerAction: 'all',
41592                 emptyText:'Add tag',
41593                 selectOnFocus:true,
41594                 width:135,
41595                 listeners : {
41596                     'select': function(c, r, i) {
41597                         editor.insertTag(r.get('tag'));
41598                         editor.focus();
41599                     }
41600                 }
41601
41602             });
41603             tb.addField(this.formatCombo);
41604             
41605         }
41606         
41607         if(!this.disable.format){
41608             tb.add(
41609                 btn('bold'),
41610                 btn('italic'),
41611                 btn('underline')
41612             );
41613         };
41614         if(!this.disable.fontSize){
41615             tb.add(
41616                 '-',
41617                 
41618                 
41619                 btn('increasefontsize', false, editor.adjustFont),
41620                 btn('decreasefontsize', false, editor.adjustFont)
41621             );
41622         };
41623         
41624         
41625         if(!this.disable.colors){
41626             tb.add(
41627                 '-', {
41628                     id:editor.frameId +'-forecolor',
41629                     cls:'x-btn-icon x-edit-forecolor',
41630                     clickEvent:'mousedown',
41631                     tooltip: this.buttonTips['forecolor'] || undefined,
41632                     tabIndex:-1,
41633                     menu : new Roo.menu.ColorMenu({
41634                         allowReselect: true,
41635                         focus: Roo.emptyFn,
41636                         value:'000000',
41637                         plain:true,
41638                         selectHandler: function(cp, color){
41639                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
41640                             editor.deferFocus();
41641                         },
41642                         scope: editor,
41643                         clickEvent:'mousedown'
41644                     })
41645                 }, {
41646                     id:editor.frameId +'backcolor',
41647                     cls:'x-btn-icon x-edit-backcolor',
41648                     clickEvent:'mousedown',
41649                     tooltip: this.buttonTips['backcolor'] || undefined,
41650                     tabIndex:-1,
41651                     menu : new Roo.menu.ColorMenu({
41652                         focus: Roo.emptyFn,
41653                         value:'FFFFFF',
41654                         plain:true,
41655                         allowReselect: true,
41656                         selectHandler: function(cp, color){
41657                             if(Roo.isGecko){
41658                                 editor.execCmd('useCSS', false);
41659                                 editor.execCmd('hilitecolor', color);
41660                                 editor.execCmd('useCSS', true);
41661                                 editor.deferFocus();
41662                             }else{
41663                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
41664                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
41665                                 editor.deferFocus();
41666                             }
41667                         },
41668                         scope:editor,
41669                         clickEvent:'mousedown'
41670                     })
41671                 }
41672             );
41673         };
41674         // now add all the items...
41675         
41676
41677         if(!this.disable.alignments){
41678             tb.add(
41679                 '-',
41680                 btn('justifyleft'),
41681                 btn('justifycenter'),
41682                 btn('justifyright')
41683             );
41684         };
41685
41686         //if(!Roo.isSafari){
41687             if(!this.disable.links){
41688                 tb.add(
41689                     '-',
41690                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
41691                 );
41692             };
41693
41694             if(!this.disable.lists){
41695                 tb.add(
41696                     '-',
41697                     btn('insertorderedlist'),
41698                     btn('insertunorderedlist')
41699                 );
41700             }
41701             if(!this.disable.sourceEdit){
41702                 tb.add(
41703                     '-',
41704                     btn('sourceedit', true, function(btn){
41705                         this.toggleSourceEdit(btn.pressed);
41706                     })
41707                 );
41708             }
41709         //}
41710         
41711         var smenu = { };
41712         // special menu.. - needs to be tidied up..
41713         if (!this.disable.special) {
41714             smenu = {
41715                 text: "&#169;",
41716                 cls: 'x-edit-none',
41717                 
41718                 menu : {
41719                     items : []
41720                 }
41721             };
41722             for (var i =0; i < this.specialChars.length; i++) {
41723                 smenu.menu.items.push({
41724                     
41725                     html: this.specialChars[i],
41726                     handler: function(a,b) {
41727                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
41728                         //editor.insertAtCursor(a.html);
41729                         
41730                     },
41731                     tabIndex:-1
41732                 });
41733             }
41734             
41735             
41736             tb.add(smenu);
41737             
41738             
41739         }
41740          
41741         if (!this.disable.specialElements) {
41742             var semenu = {
41743                 text: "Other;",
41744                 cls: 'x-edit-none',
41745                 menu : {
41746                     items : []
41747                 }
41748             };
41749             for (var i =0; i < this.specialElements.length; i++) {
41750                 semenu.menu.items.push(
41751                     Roo.apply({ 
41752                         handler: function(a,b) {
41753                             editor.insertAtCursor(this.ihtml);
41754                         }
41755                     }, this.specialElements[i])
41756                 );
41757                     
41758             }
41759             
41760             tb.add(semenu);
41761             
41762             
41763         }
41764          
41765         
41766         if (this.btns) {
41767             for(var i =0; i< this.btns.length;i++) {
41768                 var b = Roo.factory(this.btns[i],Roo.form);
41769                 b.cls =  'x-edit-none';
41770                 b.scope = editor;
41771                 tb.add(b);
41772             }
41773         
41774         }
41775         
41776         
41777         
41778         // disable everything...
41779         
41780         this.tb.items.each(function(item){
41781            if(item.id != editor.frameId+ '-sourceedit'){
41782                 item.disable();
41783             }
41784         });
41785         this.rendered = true;
41786         
41787         // the all the btns;
41788         editor.on('editorevent', this.updateToolbar, this);
41789         // other toolbars need to implement this..
41790         //editor.on('editmodechange', this.updateToolbar, this);
41791     },
41792     
41793     
41794     
41795     /**
41796      * Protected method that will not generally be called directly. It triggers
41797      * a toolbar update by reading the markup state of the current selection in the editor.
41798      */
41799     updateToolbar: function(){
41800
41801         if(!this.editor.activated){
41802             this.editor.onFirstFocus();
41803             return;
41804         }
41805
41806         var btns = this.tb.items.map, 
41807             doc = this.editor.doc,
41808             frameId = this.editor.frameId;
41809
41810         if(!this.disable.font && !Roo.isSafari){
41811             /*
41812             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
41813             if(name != this.fontSelect.dom.value){
41814                 this.fontSelect.dom.value = name;
41815             }
41816             */
41817         }
41818         if(!this.disable.format){
41819             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
41820             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
41821             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
41822         }
41823         if(!this.disable.alignments){
41824             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
41825             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
41826             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
41827         }
41828         if(!Roo.isSafari && !this.disable.lists){
41829             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
41830             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
41831         }
41832         
41833         var ans = this.editor.getAllAncestors();
41834         if (this.formatCombo) {
41835             
41836             
41837             var store = this.formatCombo.store;
41838             this.formatCombo.setValue("");
41839             for (var i =0; i < ans.length;i++) {
41840                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
41841                     // select it..
41842                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
41843                     break;
41844                 }
41845             }
41846         }
41847         
41848         
41849         
41850         // hides menus... - so this cant be on a menu...
41851         Roo.menu.MenuMgr.hideAll();
41852
41853         //this.editorsyncValue();
41854     },
41855    
41856     
41857     createFontOptions : function(){
41858         var buf = [], fs = this.fontFamilies, ff, lc;
41859         for(var i = 0, len = fs.length; i< len; i++){
41860             ff = fs[i];
41861             lc = ff.toLowerCase();
41862             buf.push(
41863                 '<option value="',lc,'" style="font-family:',ff,';"',
41864                     (this.defaultFont == lc ? ' selected="true">' : '>'),
41865                     ff,
41866                 '</option>'
41867             );
41868         }
41869         return buf.join('');
41870     },
41871     
41872     toggleSourceEdit : function(sourceEditMode){
41873         if(sourceEditMode === undefined){
41874             sourceEditMode = !this.sourceEditMode;
41875         }
41876         this.sourceEditMode = sourceEditMode === true;
41877         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
41878         // just toggle the button?
41879         if(btn.pressed !== this.editor.sourceEditMode){
41880             btn.toggle(this.editor.sourceEditMode);
41881             return;
41882         }
41883         
41884         if(this.sourceEditMode){
41885             this.tb.items.each(function(item){
41886                 if(item.cmd != 'sourceedit'){
41887                     item.disable();
41888                 }
41889             });
41890           
41891         }else{
41892             if(this.initialized){
41893                 this.tb.items.each(function(item){
41894                     item.enable();
41895                 });
41896             }
41897             
41898         }
41899         // tell the editor that it's been pressed..
41900         this.editor.toggleSourceEdit(sourceEditMode);
41901        
41902     },
41903      /**
41904      * Object collection of toolbar tooltips for the buttons in the editor. The key
41905      * is the command id associated with that button and the value is a valid QuickTips object.
41906      * For example:
41907 <pre><code>
41908 {
41909     bold : {
41910         title: 'Bold (Ctrl+B)',
41911         text: 'Make the selected text bold.',
41912         cls: 'x-html-editor-tip'
41913     },
41914     italic : {
41915         title: 'Italic (Ctrl+I)',
41916         text: 'Make the selected text italic.',
41917         cls: 'x-html-editor-tip'
41918     },
41919     ...
41920 </code></pre>
41921     * @type Object
41922      */
41923     buttonTips : {
41924         bold : {
41925             title: 'Bold (Ctrl+B)',
41926             text: 'Make the selected text bold.',
41927             cls: 'x-html-editor-tip'
41928         },
41929         italic : {
41930             title: 'Italic (Ctrl+I)',
41931             text: 'Make the selected text italic.',
41932             cls: 'x-html-editor-tip'
41933         },
41934         underline : {
41935             title: 'Underline (Ctrl+U)',
41936             text: 'Underline the selected text.',
41937             cls: 'x-html-editor-tip'
41938         },
41939         increasefontsize : {
41940             title: 'Grow Text',
41941             text: 'Increase the font size.',
41942             cls: 'x-html-editor-tip'
41943         },
41944         decreasefontsize : {
41945             title: 'Shrink Text',
41946             text: 'Decrease the font size.',
41947             cls: 'x-html-editor-tip'
41948         },
41949         backcolor : {
41950             title: 'Text Highlight Color',
41951             text: 'Change the background color of the selected text.',
41952             cls: 'x-html-editor-tip'
41953         },
41954         forecolor : {
41955             title: 'Font Color',
41956             text: 'Change the color of the selected text.',
41957             cls: 'x-html-editor-tip'
41958         },
41959         justifyleft : {
41960             title: 'Align Text Left',
41961             text: 'Align text to the left.',
41962             cls: 'x-html-editor-tip'
41963         },
41964         justifycenter : {
41965             title: 'Center Text',
41966             text: 'Center text in the editor.',
41967             cls: 'x-html-editor-tip'
41968         },
41969         justifyright : {
41970             title: 'Align Text Right',
41971             text: 'Align text to the right.',
41972             cls: 'x-html-editor-tip'
41973         },
41974         insertunorderedlist : {
41975             title: 'Bullet List',
41976             text: 'Start a bulleted list.',
41977             cls: 'x-html-editor-tip'
41978         },
41979         insertorderedlist : {
41980             title: 'Numbered List',
41981             text: 'Start a numbered list.',
41982             cls: 'x-html-editor-tip'
41983         },
41984         createlink : {
41985             title: 'Hyperlink',
41986             text: 'Make the selected text a hyperlink.',
41987             cls: 'x-html-editor-tip'
41988         },
41989         sourceedit : {
41990             title: 'Source Edit',
41991             text: 'Switch to source editing mode.',
41992             cls: 'x-html-editor-tip'
41993         }
41994     },
41995     // private
41996     onDestroy : function(){
41997         if(this.rendered){
41998             
41999             this.tb.items.each(function(item){
42000                 if(item.menu){
42001                     item.menu.removeAll();
42002                     if(item.menu.el){
42003                         item.menu.el.destroy();
42004                     }
42005                 }
42006                 item.destroy();
42007             });
42008              
42009         }
42010     },
42011     onFirstFocus: function() {
42012         this.tb.items.each(function(item){
42013            item.enable();
42014         });
42015     }
42016 });
42017
42018
42019
42020
42021 // <script type="text/javascript">
42022 /*
42023  * Based on
42024  * Ext JS Library 1.1.1
42025  * Copyright(c) 2006-2007, Ext JS, LLC.
42026  *  
42027  
42028  */
42029
42030  
42031 /**
42032  * @class Roo.form.HtmlEditor.ToolbarContext
42033  * Context Toolbar
42034  * 
42035  * Usage:
42036  *
42037  new Roo.form.HtmlEditor({
42038     ....
42039     toolbars : [
42040         { xtype: 'ToolbarStandard', styles : {} }
42041         { xtype: 'ToolbarContext', disable : {} }
42042     ]
42043 })
42044
42045      
42046  * 
42047  * @config : {Object} disable List of elements to disable.. (not done yet.)
42048  * @config : {Object} styles  Map of styles available.
42049  * 
42050  */
42051
42052 Roo.form.HtmlEditor.ToolbarContext = function(config)
42053 {
42054     
42055     Roo.apply(this, config);
42056     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42057     // dont call parent... till later.
42058     this.styles = this.styles || {};
42059 }
42060 Roo.form.HtmlEditor.ToolbarContext.types = {
42061     'IMG' : {
42062         width : {
42063             title: "Width",
42064             width: 40
42065         },
42066         height:  {
42067             title: "Height",
42068             width: 40
42069         },
42070         align: {
42071             title: "Align",
42072             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42073             width : 80
42074             
42075         },
42076         border: {
42077             title: "Border",
42078             width: 40
42079         },
42080         alt: {
42081             title: "Alt",
42082             width: 120
42083         },
42084         src : {
42085             title: "Src",
42086             width: 220
42087         }
42088         
42089     },
42090     'A' : {
42091         name : {
42092             title: "Name",
42093             width: 50
42094         },
42095         href:  {
42096             title: "Href",
42097             width: 220
42098         } // border?
42099         
42100     },
42101     'TABLE' : {
42102         rows : {
42103             title: "Rows",
42104             width: 20
42105         },
42106         cols : {
42107             title: "Cols",
42108             width: 20
42109         },
42110         width : {
42111             title: "Width",
42112             width: 40
42113         },
42114         height : {
42115             title: "Height",
42116             width: 40
42117         },
42118         border : {
42119             title: "Border",
42120             width: 20
42121         }
42122     },
42123     'TD' : {
42124         width : {
42125             title: "Width",
42126             width: 40
42127         },
42128         height : {
42129             title: "Height",
42130             width: 40
42131         },   
42132         align: {
42133             title: "Align",
42134             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42135             width: 80
42136         },
42137         valign: {
42138             title: "Valign",
42139             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
42140             width: 80
42141         },
42142         colspan: {
42143             title: "Colspan",
42144             width: 20
42145             
42146         }
42147     },
42148     'INPUT' : {
42149         name : {
42150             title: "name",
42151             width: 120
42152         },
42153         value : {
42154             title: "Value",
42155             width: 120
42156         },
42157         width : {
42158             title: "Width",
42159             width: 40
42160         }
42161     },
42162     'LABEL' : {
42163         'for' : {
42164             title: "For",
42165             width: 120
42166         }
42167     },
42168     'TEXTAREA' : {
42169           name : {
42170             title: "name",
42171             width: 120
42172         },
42173         rows : {
42174             title: "Rows",
42175             width: 20
42176         },
42177         cols : {
42178             title: "Cols",
42179             width: 20
42180         }
42181     },
42182     'SELECT' : {
42183         name : {
42184             title: "name",
42185             width: 120
42186         },
42187         selectoptions : {
42188             title: "Options",
42189             width: 200
42190         }
42191     },
42192     
42193     // should we really allow this??
42194     // should this just be 
42195     'BODY' : {
42196         title : {
42197             title: "title",
42198             width: 200,
42199             disabled : true
42200         }
42201     },
42202     '*' : {
42203         // empty..
42204     }
42205 };
42206
42207
42208
42209 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
42210     
42211     tb: false,
42212     
42213     rendered: false,
42214     
42215     editor : false,
42216     /**
42217      * @cfg {Object} disable  List of toolbar elements to disable
42218          
42219      */
42220     disable : false,
42221     /**
42222      * @cfg {Object} styles List of styles 
42223      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
42224      *
42225      * These must be defined in the page, so they get rendered correctly..
42226      * .headline { }
42227      * TD.underline { }
42228      * 
42229      */
42230     styles : false,
42231     
42232     
42233     
42234     toolbars : false,
42235     
42236     init : function(editor)
42237     {
42238         this.editor = editor;
42239         
42240         
42241         var fid = editor.frameId;
42242         var etb = this;
42243         function btn(id, toggle, handler){
42244             var xid = fid + '-'+ id ;
42245             return {
42246                 id : xid,
42247                 cmd : id,
42248                 cls : 'x-btn-icon x-edit-'+id,
42249                 enableToggle:toggle !== false,
42250                 scope: editor, // was editor...
42251                 handler:handler||editor.relayBtnCmd,
42252                 clickEvent:'mousedown',
42253                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42254                 tabIndex:-1
42255             };
42256         }
42257         // create a new element.
42258         var wdiv = editor.wrap.createChild({
42259                 tag: 'div'
42260             }, editor.wrap.dom.firstChild.nextSibling, true);
42261         
42262         // can we do this more than once??
42263         
42264          // stop form submits
42265       
42266  
42267         // disable everything...
42268         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42269         this.toolbars = {};
42270            
42271         for (var i in  ty) {
42272           
42273             this.toolbars[i] = this.buildToolbar(ty[i],i);
42274         }
42275         this.tb = this.toolbars.BODY;
42276         this.tb.el.show();
42277         this.buildFooter();
42278         this.footer.show();
42279         editor.on('hide', function( ) { this.footer.hide() }, this);
42280         editor.on('show', function( ) { this.footer.show() }, this);
42281         
42282          
42283         this.rendered = true;
42284         
42285         // the all the btns;
42286         editor.on('editorevent', this.updateToolbar, this);
42287         // other toolbars need to implement this..
42288         //editor.on('editmodechange', this.updateToolbar, this);
42289     },
42290     
42291     
42292     
42293     /**
42294      * Protected method that will not generally be called directly. It triggers
42295      * a toolbar update by reading the markup state of the current selection in the editor.
42296      */
42297     updateToolbar: function(editor,ev,sel){
42298
42299         //Roo.log(ev);
42300         // capture mouse up - this is handy for selecting images..
42301         // perhaps should go somewhere else...
42302         if(!this.editor.activated){
42303              this.editor.onFirstFocus();
42304             return;
42305         }
42306         
42307         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
42308         // selectNode - might want to handle IE?
42309         if (ev &&
42310             (ev.type == 'mouseup' || ev.type == 'click' ) &&
42311             ev.target && ev.target.tagName == 'IMG') {
42312             // they have click on an image...
42313             // let's see if we can change the selection...
42314             sel = ev.target;
42315          
42316               var nodeRange = sel.ownerDocument.createRange();
42317             try {
42318                 nodeRange.selectNode(sel);
42319             } catch (e) {
42320                 nodeRange.selectNodeContents(sel);
42321             }
42322             //nodeRange.collapse(true);
42323             var s = editor.win.getSelection();
42324             s.removeAllRanges();
42325             s.addRange(nodeRange);
42326         }  
42327         
42328       
42329         var updateFooter = sel ? false : true;
42330         
42331         
42332         var ans = this.editor.getAllAncestors();
42333         
42334         // pick
42335         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42336         
42337         if (!sel) { 
42338             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
42339             sel = sel ? sel : this.editor.doc.body;
42340             sel = sel.tagName.length ? sel : this.editor.doc.body;
42341             
42342         }
42343         // pick a menu that exists..
42344         var tn = sel.tagName.toUpperCase();
42345         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
42346         
42347         tn = sel.tagName.toUpperCase();
42348         
42349         var lastSel = this.tb.selectedNode
42350         
42351         this.tb.selectedNode = sel;
42352         
42353         // if current menu does not match..
42354         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
42355                 
42356             this.tb.el.hide();
42357             ///console.log("show: " + tn);
42358             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
42359             this.tb.el.show();
42360             // update name
42361             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
42362             
42363             
42364             // update attributes
42365             if (this.tb.fields) {
42366                 this.tb.fields.each(function(e) {
42367                    e.setValue(sel.getAttribute(e.attrname));
42368                 });
42369             }
42370             
42371             var hasStyles = false;
42372             for(var i in this.styles) {
42373                 hasStyles = true;
42374                 break;
42375             }
42376             
42377             // update styles
42378             if (hasStyles) { 
42379                 var st = this.tb.fields.item(0);
42380                 
42381                 st.store.removeAll();
42382                
42383                 
42384                 var cn = sel.className.split(/\s+/);
42385                 
42386                 var avs = [];
42387                 if (this.styles['*']) {
42388                     
42389                     Roo.each(this.styles['*'], function(v) {
42390                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42391                     });
42392                 }
42393                 if (this.styles[tn]) { 
42394                     Roo.each(this.styles[tn], function(v) {
42395                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42396                     });
42397                 }
42398                 
42399                 st.store.loadData(avs);
42400                 st.collapse();
42401                 st.setValue(cn);
42402             }
42403             // flag our selected Node.
42404             this.tb.selectedNode = sel;
42405            
42406            
42407             Roo.menu.MenuMgr.hideAll();
42408
42409         }
42410         
42411         if (!updateFooter) {
42412             //this.footDisp.dom.innerHTML = ''; 
42413             return;
42414         }
42415         // update the footer
42416         //
42417         var html = '';
42418         
42419         this.footerEls = ans.reverse();
42420         Roo.each(this.footerEls, function(a,i) {
42421             if (!a) { return; }
42422             html += html.length ? ' &gt; '  :  '';
42423             
42424             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
42425             
42426         });
42427        
42428         // 
42429         var sz = this.footDisp.up('td').getSize();
42430         this.footDisp.dom.style.width = (sz.width -10) + 'px';
42431         this.footDisp.dom.style.marginLeft = '5px';
42432         
42433         this.footDisp.dom.style.overflow = 'hidden';
42434         
42435         this.footDisp.dom.innerHTML = html;
42436             
42437         //this.editorsyncValue();
42438     },
42439      
42440     
42441    
42442        
42443     // private
42444     onDestroy : function(){
42445         if(this.rendered){
42446             
42447             this.tb.items.each(function(item){
42448                 if(item.menu){
42449                     item.menu.removeAll();
42450                     if(item.menu.el){
42451                         item.menu.el.destroy();
42452                     }
42453                 }
42454                 item.destroy();
42455             });
42456              
42457         }
42458     },
42459     onFirstFocus: function() {
42460         // need to do this for all the toolbars..
42461         this.tb.items.each(function(item){
42462            item.enable();
42463         });
42464     },
42465     buildToolbar: function(tlist, nm)
42466     {
42467         var editor = this.editor;
42468          // create a new element.
42469         var wdiv = editor.wrap.createChild({
42470                 tag: 'div'
42471             }, editor.wrap.dom.firstChild.nextSibling, true);
42472         
42473        
42474         var tb = new Roo.Toolbar(wdiv);
42475         // add the name..
42476         
42477         tb.add(nm+ ":&nbsp;");
42478         
42479         var styles = [];
42480         for(var i in this.styles) {
42481             styles.push(i);
42482         }
42483         
42484         // styles...
42485         if (styles && styles.length) {
42486             
42487             // this needs a multi-select checkbox...
42488             tb.addField( new Roo.form.ComboBox({
42489                 store: new Roo.data.SimpleStore({
42490                     id : 'val',
42491                     fields: ['val', 'selected'],
42492                     data : [] 
42493                 }),
42494                 name : '-roo-edit-className',
42495                 attrname : 'className',
42496                 displayField:'val',
42497                 typeAhead: false,
42498                 mode: 'local',
42499                 editable : false,
42500                 triggerAction: 'all',
42501                 emptyText:'Select Style',
42502                 selectOnFocus:true,
42503                 width: 130,
42504                 listeners : {
42505                     'select': function(c, r, i) {
42506                         // initial support only for on class per el..
42507                         tb.selectedNode.className =  r ? r.get('val') : '';
42508                         editor.syncValue();
42509                     }
42510                 }
42511     
42512             }));
42513         }
42514             
42515         
42516         
42517         for (var i in tlist) {
42518             
42519             var item = tlist[i];
42520             tb.add(item.title + ":&nbsp;");
42521             
42522             
42523             
42524             
42525             if (item.opts) {
42526                 // opts == pulldown..
42527                 tb.addField( new Roo.form.ComboBox({
42528                     store: new Roo.data.SimpleStore({
42529                         id : 'val',
42530                         fields: ['val'],
42531                         data : item.opts  
42532                     }),
42533                     name : '-roo-edit-' + i,
42534                     attrname : i,
42535                     displayField:'val',
42536                     typeAhead: false,
42537                     mode: 'local',
42538                     editable : false,
42539                     triggerAction: 'all',
42540                     emptyText:'Select',
42541                     selectOnFocus:true,
42542                     width: item.width ? item.width  : 130,
42543                     listeners : {
42544                         'select': function(c, r, i) {
42545                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
42546                         }
42547                     }
42548
42549                 }));
42550                 continue;
42551                     
42552                  
42553                 
42554                 tb.addField( new Roo.form.TextField({
42555                     name: i,
42556                     width: 100,
42557                     //allowBlank:false,
42558                     value: ''
42559                 }));
42560                 continue;
42561             }
42562             tb.addField( new Roo.form.TextField({
42563                 name: '-roo-edit-' + i,
42564                 attrname : i,
42565                 
42566                 width: item.width,
42567                 //allowBlank:true,
42568                 value: '',
42569                 listeners: {
42570                     'change' : function(f, nv, ov) {
42571                         tb.selectedNode.setAttribute(f.attrname, nv);
42572                     }
42573                 }
42574             }));
42575              
42576         }
42577         tb.addFill();
42578         var _this = this;
42579         tb.addButton( {
42580             text: 'Remove Tag',
42581     
42582             listeners : {
42583                 click : function ()
42584                 {
42585                     // remove
42586                     // undo does not work.
42587                      
42588                     var sn = tb.selectedNode;
42589                     Roo.log(sn);
42590                     var pn = sn.parentNode;
42591                     
42592                     var stn =  sn.childNodes[0];
42593                     var en = sn.childNodes[sn.childNodes.length - 1 ];
42594                     while (sn.childNodes.length) {
42595                         var node = sn.childNodes[0];
42596                         sn.removeChild(node);
42597                         Roo.log(node);
42598                         pn.insertBefore(node, sn);
42599                         
42600                     }
42601                     pn.removeChild(sn);
42602                     var range = editor.createRange();
42603         
42604                     range.setStart(stn,0);
42605                     range.setEnd(en,0); //????
42606                     //range.selectNode(sel);
42607                     
42608                     
42609                     var selection = editor.getSelection();
42610                     selection.removeAllRanges();
42611                     selection.addRange(range);
42612                     
42613                     
42614                     
42615                     //_this.updateToolbar(null, null, pn);
42616                     _this.updateToolbar(null, null, null);
42617                     this.footDisp.dom.innerHTML = ''; 
42618                 }
42619             }
42620             
42621                     
42622                 
42623             
42624         });
42625         
42626         
42627         tb.el.on('click', function(e){
42628             e.preventDefault(); // what does this do?
42629         });
42630         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
42631         tb.el.hide();
42632         tb.name = nm;
42633         // dont need to disable them... as they will get hidden
42634         return tb;
42635          
42636         
42637     },
42638     buildFooter : function()
42639     {
42640         
42641         var fel = this.editor.wrap.createChild();
42642         this.footer = new Roo.Toolbar(fel);
42643         // toolbar has scrolly on left / right?
42644         var footDisp= new Roo.Toolbar.Fill();
42645         var _t = this;
42646         this.footer.add(
42647             {
42648                 text : '&lt;',
42649                 xtype: 'Button',
42650                 handler : function() {
42651                     _t.footDisp.scrollTo('left',0,true)
42652                 }
42653             }
42654         );
42655         this.footer.add( footDisp );
42656         this.footer.add( 
42657             {
42658                 text : '&gt;',
42659                 xtype: 'Button',
42660                 handler : function() {
42661                     // no animation..
42662                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
42663                 }
42664             }
42665         );
42666         var fel = Roo.get(footDisp.el);
42667         fel.addClass('x-editor-context');
42668         this.footDispWrap = fel; 
42669         this.footDispWrap.overflow  = 'hidden';
42670         
42671         this.footDisp = fel.createChild();
42672         this.footDispWrap.on('click', this.onContextClick, this)
42673         
42674         
42675     },
42676     onContextClick : function (ev,dom)
42677     {
42678         ev.preventDefault();
42679         var  cn = dom.className;
42680         Roo.log(cn);
42681         if (!cn.match(/x-ed-loc-/)) {
42682             return;
42683         }
42684         var n = cn.split('-').pop();
42685         var ans = this.footerEls;
42686         var sel = ans[n];
42687         
42688          // pick
42689         var range = this.editor.createRange();
42690         
42691         range.selectNodeContents(sel);
42692         //range.selectNode(sel);
42693         
42694         
42695         var selection = this.editor.getSelection();
42696         selection.removeAllRanges();
42697         selection.addRange(range);
42698         
42699         
42700         
42701         this.updateToolbar(null, null, sel);
42702         
42703         
42704     }
42705     
42706     
42707     
42708     
42709     
42710 });
42711
42712
42713
42714
42715
42716 /*
42717  * Based on:
42718  * Ext JS Library 1.1.1
42719  * Copyright(c) 2006-2007, Ext JS, LLC.
42720  *
42721  * Originally Released Under LGPL - original licence link has changed is not relivant.
42722  *
42723  * Fork - LGPL
42724  * <script type="text/javascript">
42725  */
42726  
42727 /**
42728  * @class Roo.form.BasicForm
42729  * @extends Roo.util.Observable
42730  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
42731  * @constructor
42732  * @param {String/HTMLElement/Roo.Element} el The form element or its id
42733  * @param {Object} config Configuration options
42734  */
42735 Roo.form.BasicForm = function(el, config){
42736     this.allItems = [];
42737     this.childForms = [];
42738     Roo.apply(this, config);
42739     /*
42740      * The Roo.form.Field items in this form.
42741      * @type MixedCollection
42742      */
42743      
42744      
42745     this.items = new Roo.util.MixedCollection(false, function(o){
42746         return o.id || (o.id = Roo.id());
42747     });
42748     this.addEvents({
42749         /**
42750          * @event beforeaction
42751          * Fires before any action is performed. Return false to cancel the action.
42752          * @param {Form} this
42753          * @param {Action} action The action to be performed
42754          */
42755         beforeaction: true,
42756         /**
42757          * @event actionfailed
42758          * Fires when an action fails.
42759          * @param {Form} this
42760          * @param {Action} action The action that failed
42761          */
42762         actionfailed : true,
42763         /**
42764          * @event actioncomplete
42765          * Fires when an action is completed.
42766          * @param {Form} this
42767          * @param {Action} action The action that completed
42768          */
42769         actioncomplete : true
42770     });
42771     if(el){
42772         this.initEl(el);
42773     }
42774     Roo.form.BasicForm.superclass.constructor.call(this);
42775 };
42776
42777 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
42778     /**
42779      * @cfg {String} method
42780      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
42781      */
42782     /**
42783      * @cfg {DataReader} reader
42784      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
42785      * This is optional as there is built-in support for processing JSON.
42786      */
42787     /**
42788      * @cfg {DataReader} errorReader
42789      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
42790      * This is completely optional as there is built-in support for processing JSON.
42791      */
42792     /**
42793      * @cfg {String} url
42794      * The URL to use for form actions if one isn't supplied in the action options.
42795      */
42796     /**
42797      * @cfg {Boolean} fileUpload
42798      * Set to true if this form is a file upload.
42799      */
42800      
42801     /**
42802      * @cfg {Object} baseParams
42803      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
42804      */
42805      /**
42806      
42807     /**
42808      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
42809      */
42810     timeout: 30,
42811
42812     // private
42813     activeAction : null,
42814
42815     /**
42816      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
42817      * or setValues() data instead of when the form was first created.
42818      */
42819     trackResetOnLoad : false,
42820     
42821     
42822     /**
42823      * childForms - used for multi-tab forms
42824      * @type {Array}
42825      */
42826     childForms : false,
42827     
42828     /**
42829      * allItems - full list of fields.
42830      * @type {Array}
42831      */
42832     allItems : false,
42833     
42834     /**
42835      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
42836      * element by passing it or its id or mask the form itself by passing in true.
42837      * @type Mixed
42838      */
42839     waitMsgTarget : false,
42840
42841     // private
42842     initEl : function(el){
42843         this.el = Roo.get(el);
42844         this.id = this.el.id || Roo.id();
42845         this.el.on('submit', this.onSubmit, this);
42846         this.el.addClass('x-form');
42847     },
42848
42849     // private
42850     onSubmit : function(e){
42851         e.stopEvent();
42852     },
42853
42854     /**
42855      * Returns true if client-side validation on the form is successful.
42856      * @return Boolean
42857      */
42858     isValid : function(){
42859         var valid = true;
42860         this.items.each(function(f){
42861            if(!f.validate()){
42862                valid = false;
42863            }
42864         });
42865         return valid;
42866     },
42867
42868     /**
42869      * Returns true if any fields in this form have changed since their original load.
42870      * @return Boolean
42871      */
42872     isDirty : function(){
42873         var dirty = false;
42874         this.items.each(function(f){
42875            if(f.isDirty()){
42876                dirty = true;
42877                return false;
42878            }
42879         });
42880         return dirty;
42881     },
42882
42883     /**
42884      * Performs a predefined action (submit or load) or custom actions you define on this form.
42885      * @param {String} actionName The name of the action type
42886      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
42887      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
42888      * accept other config options):
42889      * <pre>
42890 Property          Type             Description
42891 ----------------  ---------------  ----------------------------------------------------------------------------------
42892 url               String           The url for the action (defaults to the form's url)
42893 method            String           The form method to use (defaults to the form's method, or POST if not defined)
42894 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
42895 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
42896                                    validate the form on the client (defaults to false)
42897      * </pre>
42898      * @return {BasicForm} this
42899      */
42900     doAction : function(action, options){
42901         if(typeof action == 'string'){
42902             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
42903         }
42904         if(this.fireEvent('beforeaction', this, action) !== false){
42905             this.beforeAction(action);
42906             action.run.defer(100, action);
42907         }
42908         return this;
42909     },
42910
42911     /**
42912      * Shortcut to do a submit action.
42913      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
42914      * @return {BasicForm} this
42915      */
42916     submit : function(options){
42917         this.doAction('submit', options);
42918         return this;
42919     },
42920
42921     /**
42922      * Shortcut to do a load action.
42923      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
42924      * @return {BasicForm} this
42925      */
42926     load : function(options){
42927         this.doAction('load', options);
42928         return this;
42929     },
42930
42931     /**
42932      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
42933      * @param {Record} record The record to edit
42934      * @return {BasicForm} this
42935      */
42936     updateRecord : function(record){
42937         record.beginEdit();
42938         var fs = record.fields;
42939         fs.each(function(f){
42940             var field = this.findField(f.name);
42941             if(field){
42942                 record.set(f.name, field.getValue());
42943             }
42944         }, this);
42945         record.endEdit();
42946         return this;
42947     },
42948
42949     /**
42950      * Loads an Roo.data.Record into this form.
42951      * @param {Record} record The record to load
42952      * @return {BasicForm} this
42953      */
42954     loadRecord : function(record){
42955         this.setValues(record.data);
42956         return this;
42957     },
42958
42959     // private
42960     beforeAction : function(action){
42961         var o = action.options;
42962         
42963        
42964         if(this.waitMsgTarget === true){
42965             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
42966         }else if(this.waitMsgTarget){
42967             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
42968             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
42969         }else {
42970             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
42971         }
42972          
42973     },
42974
42975     // private
42976     afterAction : function(action, success){
42977         this.activeAction = null;
42978         var o = action.options;
42979         
42980         if(this.waitMsgTarget === true){
42981             this.el.unmask();
42982         }else if(this.waitMsgTarget){
42983             this.waitMsgTarget.unmask();
42984         }else{
42985             Roo.MessageBox.updateProgress(1);
42986             Roo.MessageBox.hide();
42987         }
42988          
42989         if(success){
42990             if(o.reset){
42991                 this.reset();
42992             }
42993             Roo.callback(o.success, o.scope, [this, action]);
42994             this.fireEvent('actioncomplete', this, action);
42995             
42996         }else{
42997             
42998             // failure condition..
42999             // we have a scenario where updates need confirming.
43000             // eg. if a locking scenario exists..
43001             // we look for { errors : { needs_confirm : true }} in the response.
43002             if (
43003                 (typeof(action.result) != 'undefined')  &&
43004                 (typeof(action.result.errors) != 'undefined')  &&
43005                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43006            ){
43007                 var _t = this;
43008                 Roo.MessageBox.confirm(
43009                     "Change requires confirmation",
43010                     action.result.errorMsg,
43011                     function(r) {
43012                         if (r != 'yes') {
43013                             return;
43014                         }
43015                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43016                     }
43017                     
43018                 );
43019                 
43020                 
43021                 
43022                 return;
43023             }
43024             
43025             Roo.callback(o.failure, o.scope, [this, action]);
43026             // show an error message if no failed handler is set..
43027             if (!this.hasListener('actionfailed')) {
43028                 Roo.MessageBox.alert("Error",
43029                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43030                         action.result.errorMsg :
43031                         "Saving Failed, please check your entries or try again"
43032                 );
43033             }
43034             
43035             this.fireEvent('actionfailed', this, action);
43036         }
43037         
43038     },
43039
43040     /**
43041      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43042      * @param {String} id The value to search for
43043      * @return Field
43044      */
43045     findField : function(id){
43046         var field = this.items.get(id);
43047         if(!field){
43048             this.items.each(function(f){
43049                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43050                     field = f;
43051                     return false;
43052                 }
43053             });
43054         }
43055         return field || null;
43056     },
43057
43058     /**
43059      * Add a secondary form to this one, 
43060      * Used to provide tabbed forms. One form is primary, with hidden values 
43061      * which mirror the elements from the other forms.
43062      * 
43063      * @param {Roo.form.Form} form to add.
43064      * 
43065      */
43066     addForm : function(form)
43067     {
43068        
43069         if (this.childForms.indexOf(form) > -1) {
43070             // already added..
43071             return;
43072         }
43073         this.childForms.push(form);
43074         var n = '';
43075         Roo.each(form.allItems, function (fe) {
43076             
43077             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
43078             if (this.findField(n)) { // already added..
43079                 return;
43080             }
43081             var add = new Roo.form.Hidden({
43082                 name : n
43083             });
43084             add.render(this.el);
43085             
43086             this.add( add );
43087         }, this);
43088         
43089     },
43090     /**
43091      * Mark fields in this form invalid in bulk.
43092      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
43093      * @return {BasicForm} this
43094      */
43095     markInvalid : function(errors){
43096         if(errors instanceof Array){
43097             for(var i = 0, len = errors.length; i < len; i++){
43098                 var fieldError = errors[i];
43099                 var f = this.findField(fieldError.id);
43100                 if(f){
43101                     f.markInvalid(fieldError.msg);
43102                 }
43103             }
43104         }else{
43105             var field, id;
43106             for(id in errors){
43107                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
43108                     field.markInvalid(errors[id]);
43109                 }
43110             }
43111         }
43112         Roo.each(this.childForms || [], function (f) {
43113             f.markInvalid(errors);
43114         });
43115         
43116         return this;
43117     },
43118
43119     /**
43120      * Set values for fields in this form in bulk.
43121      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
43122      * @return {BasicForm} this
43123      */
43124     setValues : function(values){
43125         if(values instanceof Array){ // array of objects
43126             for(var i = 0, len = values.length; i < len; i++){
43127                 var v = values[i];
43128                 var f = this.findField(v.id);
43129                 if(f){
43130                     f.setValue(v.value);
43131                     if(this.trackResetOnLoad){
43132                         f.originalValue = f.getValue();
43133                     }
43134                 }
43135             }
43136         }else{ // object hash
43137             var field, id;
43138             for(id in values){
43139                 if(typeof values[id] != 'function' && (field = this.findField(id))){
43140                     
43141                     if (field.setFromData && 
43142                         field.valueField && 
43143                         field.displayField &&
43144                         // combos' with local stores can 
43145                         // be queried via setValue()
43146                         // to set their value..
43147                         (field.store && !field.store.isLocal)
43148                         ) {
43149                         // it's a combo
43150                         var sd = { };
43151                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
43152                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
43153                         field.setFromData(sd);
43154                         
43155                     } else {
43156                         field.setValue(values[id]);
43157                     }
43158                     
43159                     
43160                     if(this.trackResetOnLoad){
43161                         field.originalValue = field.getValue();
43162                     }
43163                 }
43164             }
43165         }
43166          
43167         Roo.each(this.childForms || [], function (f) {
43168             f.setValues(values);
43169         });
43170                 
43171         return this;
43172     },
43173
43174     /**
43175      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
43176      * they are returned as an array.
43177      * @param {Boolean} asString
43178      * @return {Object}
43179      */
43180     getValues : function(asString){
43181         if (this.childForms) {
43182             // copy values from the child forms
43183             Roo.each(this.childForms, function (f) {
43184                 this.setValues(f.getValues());
43185             }, this);
43186         }
43187         
43188         
43189         
43190         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
43191         if(asString === true){
43192             return fs;
43193         }
43194         return Roo.urlDecode(fs);
43195     },
43196     
43197     /**
43198      * Returns the fields in this form as an object with key/value pairs. 
43199      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
43200      * @return {Object}
43201      */
43202     getFieldValues : function(with_hidden)
43203     {
43204         if (this.childForms) {
43205             // copy values from the child forms
43206             // should this call getFieldValues - probably not as we do not currently copy
43207             // hidden fields when we generate..
43208             Roo.each(this.childForms, function (f) {
43209                 this.setValues(f.getValues());
43210             }, this);
43211         }
43212         
43213         var ret = {};
43214         this.items.each(function(f){
43215             if (!f.getName()) {
43216                 return;
43217             }
43218             var v = f.getValue();
43219             // not sure if this supported any more..
43220             if ((typeof(v) == 'object') && f.getRawValue) {
43221                 v = f.getRawValue() ; // dates..
43222             }
43223             // combo boxes where name != hiddenName...
43224             if (f.name != f.getName()) {
43225                 ret[f.name] = f.getRawValue();
43226             }
43227             ret[f.getName()] = v;
43228         });
43229         
43230         return ret;
43231     },
43232
43233     /**
43234      * Clears all invalid messages in this form.
43235      * @return {BasicForm} this
43236      */
43237     clearInvalid : function(){
43238         this.items.each(function(f){
43239            f.clearInvalid();
43240         });
43241         
43242         Roo.each(this.childForms || [], function (f) {
43243             f.clearInvalid();
43244         });
43245         
43246         
43247         return this;
43248     },
43249
43250     /**
43251      * Resets this form.
43252      * @return {BasicForm} this
43253      */
43254     reset : function(){
43255         this.items.each(function(f){
43256             f.reset();
43257         });
43258         
43259         Roo.each(this.childForms || [], function (f) {
43260             f.reset();
43261         });
43262        
43263         
43264         return this;
43265     },
43266
43267     /**
43268      * Add Roo.form components to this form.
43269      * @param {Field} field1
43270      * @param {Field} field2 (optional)
43271      * @param {Field} etc (optional)
43272      * @return {BasicForm} this
43273      */
43274     add : function(){
43275         this.items.addAll(Array.prototype.slice.call(arguments, 0));
43276         return this;
43277     },
43278
43279
43280     /**
43281      * Removes a field from the items collection (does NOT remove its markup).
43282      * @param {Field} field
43283      * @return {BasicForm} this
43284      */
43285     remove : function(field){
43286         this.items.remove(field);
43287         return this;
43288     },
43289
43290     /**
43291      * Looks at the fields in this form, checks them for an id attribute,
43292      * and calls applyTo on the existing dom element with that id.
43293      * @return {BasicForm} this
43294      */
43295     render : function(){
43296         this.items.each(function(f){
43297             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
43298                 f.applyTo(f.id);
43299             }
43300         });
43301         return this;
43302     },
43303
43304     /**
43305      * Calls {@link Ext#apply} for all fields in this form with the passed object.
43306      * @param {Object} values
43307      * @return {BasicForm} this
43308      */
43309     applyToFields : function(o){
43310         this.items.each(function(f){
43311            Roo.apply(f, o);
43312         });
43313         return this;
43314     },
43315
43316     /**
43317      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
43318      * @param {Object} values
43319      * @return {BasicForm} this
43320      */
43321     applyIfToFields : function(o){
43322         this.items.each(function(f){
43323            Roo.applyIf(f, o);
43324         });
43325         return this;
43326     }
43327 });
43328
43329 // back compat
43330 Roo.BasicForm = Roo.form.BasicForm;/*
43331  * Based on:
43332  * Ext JS Library 1.1.1
43333  * Copyright(c) 2006-2007, Ext JS, LLC.
43334  *
43335  * Originally Released Under LGPL - original licence link has changed is not relivant.
43336  *
43337  * Fork - LGPL
43338  * <script type="text/javascript">
43339  */
43340
43341 /**
43342  * @class Roo.form.Form
43343  * @extends Roo.form.BasicForm
43344  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
43345  * @constructor
43346  * @param {Object} config Configuration options
43347  */
43348 Roo.form.Form = function(config){
43349     var xitems =  [];
43350     if (config.items) {
43351         xitems = config.items;
43352         delete config.items;
43353     }
43354    
43355     
43356     Roo.form.Form.superclass.constructor.call(this, null, config);
43357     this.url = this.url || this.action;
43358     if(!this.root){
43359         this.root = new Roo.form.Layout(Roo.applyIf({
43360             id: Roo.id()
43361         }, config));
43362     }
43363     this.active = this.root;
43364     /**
43365      * Array of all the buttons that have been added to this form via {@link addButton}
43366      * @type Array
43367      */
43368     this.buttons = [];
43369     this.allItems = [];
43370     this.addEvents({
43371         /**
43372          * @event clientvalidation
43373          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
43374          * @param {Form} this
43375          * @param {Boolean} valid true if the form has passed client-side validation
43376          */
43377         clientvalidation: true,
43378         /**
43379          * @event rendered
43380          * Fires when the form is rendered
43381          * @param {Roo.form.Form} form
43382          */
43383         rendered : true
43384     });
43385     
43386     if (this.progressUrl) {
43387             // push a hidden field onto the list of fields..
43388             this.addxtype( {
43389                     xns: Roo.form, 
43390                     xtype : 'Hidden', 
43391                     name : 'UPLOAD_IDENTIFIER' 
43392             });
43393         }
43394         
43395     
43396     Roo.each(xitems, this.addxtype, this);
43397     
43398     
43399     
43400 };
43401
43402 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
43403     /**
43404      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
43405      */
43406     /**
43407      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
43408      */
43409     /**
43410      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
43411      */
43412     buttonAlign:'center',
43413
43414     /**
43415      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
43416      */
43417     minButtonWidth:75,
43418
43419     /**
43420      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
43421      * This property cascades to child containers if not set.
43422      */
43423     labelAlign:'left',
43424
43425     /**
43426      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
43427      * fires a looping event with that state. This is required to bind buttons to the valid
43428      * state using the config value formBind:true on the button.
43429      */
43430     monitorValid : false,
43431
43432     /**
43433      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
43434      */
43435     monitorPoll : 200,
43436     
43437     /**
43438      * @cfg {String} progressUrl - Url to return progress data 
43439      */
43440     
43441     progressUrl : false,
43442   
43443     /**
43444      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
43445      * fields are added and the column is closed. If no fields are passed the column remains open
43446      * until end() is called.
43447      * @param {Object} config The config to pass to the column
43448      * @param {Field} field1 (optional)
43449      * @param {Field} field2 (optional)
43450      * @param {Field} etc (optional)
43451      * @return Column The column container object
43452      */
43453     column : function(c){
43454         var col = new Roo.form.Column(c);
43455         this.start(col);
43456         if(arguments.length > 1){ // duplicate code required because of Opera
43457             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43458             this.end();
43459         }
43460         return col;
43461     },
43462
43463     /**
43464      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
43465      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
43466      * until end() is called.
43467      * @param {Object} config The config to pass to the fieldset
43468      * @param {Field} field1 (optional)
43469      * @param {Field} field2 (optional)
43470      * @param {Field} etc (optional)
43471      * @return FieldSet The fieldset container object
43472      */
43473     fieldset : function(c){
43474         var fs = new Roo.form.FieldSet(c);
43475         this.start(fs);
43476         if(arguments.length > 1){ // duplicate code required because of Opera
43477             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43478             this.end();
43479         }
43480         return fs;
43481     },
43482
43483     /**
43484      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
43485      * fields are added and the container is closed. If no fields are passed the container remains open
43486      * until end() is called.
43487      * @param {Object} config The config to pass to the Layout
43488      * @param {Field} field1 (optional)
43489      * @param {Field} field2 (optional)
43490      * @param {Field} etc (optional)
43491      * @return Layout The container object
43492      */
43493     container : function(c){
43494         var l = new Roo.form.Layout(c);
43495         this.start(l);
43496         if(arguments.length > 1){ // duplicate code required because of Opera
43497             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43498             this.end();
43499         }
43500         return l;
43501     },
43502
43503     /**
43504      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
43505      * @param {Object} container A Roo.form.Layout or subclass of Layout
43506      * @return {Form} this
43507      */
43508     start : function(c){
43509         // cascade label info
43510         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
43511         this.active.stack.push(c);
43512         c.ownerCt = this.active;
43513         this.active = c;
43514         return this;
43515     },
43516
43517     /**
43518      * Closes the current open container
43519      * @return {Form} this
43520      */
43521     end : function(){
43522         if(this.active == this.root){
43523             return this;
43524         }
43525         this.active = this.active.ownerCt;
43526         return this;
43527     },
43528
43529     /**
43530      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
43531      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
43532      * as the label of the field.
43533      * @param {Field} field1
43534      * @param {Field} field2 (optional)
43535      * @param {Field} etc. (optional)
43536      * @return {Form} this
43537      */
43538     add : function(){
43539         this.active.stack.push.apply(this.active.stack, arguments);
43540         this.allItems.push.apply(this.allItems,arguments);
43541         var r = [];
43542         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
43543             if(a[i].isFormField){
43544                 r.push(a[i]);
43545             }
43546         }
43547         if(r.length > 0){
43548             Roo.form.Form.superclass.add.apply(this, r);
43549         }
43550         return this;
43551     },
43552     
43553
43554     
43555     
43556     
43557      /**
43558      * Find any element that has been added to a form, using it's ID or name
43559      * This can include framesets, columns etc. along with regular fields..
43560      * @param {String} id - id or name to find.
43561      
43562      * @return {Element} e - or false if nothing found.
43563      */
43564     findbyId : function(id)
43565     {
43566         var ret = false;
43567         if (!id) {
43568             return ret;
43569         }
43570         Roo.each(this.allItems, function(f){
43571             if (f.id == id || f.name == id ){
43572                 ret = f;
43573                 return false;
43574             }
43575         });
43576         return ret;
43577     },
43578
43579     
43580     
43581     /**
43582      * Render this form into the passed container. This should only be called once!
43583      * @param {String/HTMLElement/Element} container The element this component should be rendered into
43584      * @return {Form} this
43585      */
43586     render : function(ct)
43587     {
43588         
43589         
43590         
43591         ct = Roo.get(ct);
43592         var o = this.autoCreate || {
43593             tag: 'form',
43594             method : this.method || 'POST',
43595             id : this.id || Roo.id()
43596         };
43597         this.initEl(ct.createChild(o));
43598
43599         this.root.render(this.el);
43600         
43601        
43602              
43603         this.items.each(function(f){
43604             f.render('x-form-el-'+f.id);
43605         });
43606
43607         if(this.buttons.length > 0){
43608             // tables are required to maintain order and for correct IE layout
43609             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
43610                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
43611                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
43612             }}, null, true);
43613             var tr = tb.getElementsByTagName('tr')[0];
43614             for(var i = 0, len = this.buttons.length; i < len; i++) {
43615                 var b = this.buttons[i];
43616                 var td = document.createElement('td');
43617                 td.className = 'x-form-btn-td';
43618                 b.render(tr.appendChild(td));
43619             }
43620         }
43621         if(this.monitorValid){ // initialize after render
43622             this.startMonitoring();
43623         }
43624         this.fireEvent('rendered', this);
43625         return this;
43626     },
43627
43628     /**
43629      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
43630      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
43631      * object or a valid Roo.DomHelper element config
43632      * @param {Function} handler The function called when the button is clicked
43633      * @param {Object} scope (optional) The scope of the handler function
43634      * @return {Roo.Button}
43635      */
43636     addButton : function(config, handler, scope){
43637         var bc = {
43638             handler: handler,
43639             scope: scope,
43640             minWidth: this.minButtonWidth,
43641             hideParent:true
43642         };
43643         if(typeof config == "string"){
43644             bc.text = config;
43645         }else{
43646             Roo.apply(bc, config);
43647         }
43648         var btn = new Roo.Button(null, bc);
43649         this.buttons.push(btn);
43650         return btn;
43651     },
43652
43653      /**
43654      * Adds a series of form elements (using the xtype property as the factory method.
43655      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
43656      * @param {Object} config 
43657      */
43658     
43659     addxtype : function()
43660     {
43661         var ar = Array.prototype.slice.call(arguments, 0);
43662         var ret = false;
43663         for(var i = 0; i < ar.length; i++) {
43664             if (!ar[i]) {
43665                 continue; // skip -- if this happends something invalid got sent, we 
43666                 // should ignore it, as basically that interface element will not show up
43667                 // and that should be pretty obvious!!
43668             }
43669             
43670             if (Roo.form[ar[i].xtype]) {
43671                 ar[i].form = this;
43672                 var fe = Roo.factory(ar[i], Roo.form);
43673                 if (!ret) {
43674                     ret = fe;
43675                 }
43676                 fe.form = this;
43677                 if (fe.store) {
43678                     fe.store.form = this;
43679                 }
43680                 if (fe.isLayout) {  
43681                          
43682                     this.start(fe);
43683                     this.allItems.push(fe);
43684                     if (fe.items && fe.addxtype) {
43685                         fe.addxtype.apply(fe, fe.items);
43686                         delete fe.items;
43687                     }
43688                      this.end();
43689                     continue;
43690                 }
43691                 
43692                 
43693                  
43694                 this.add(fe);
43695               //  console.log('adding ' + ar[i].xtype);
43696             }
43697             if (ar[i].xtype == 'Button') {  
43698                 //console.log('adding button');
43699                 //console.log(ar[i]);
43700                 this.addButton(ar[i]);
43701                 this.allItems.push(fe);
43702                 continue;
43703             }
43704             
43705             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
43706                 alert('end is not supported on xtype any more, use items');
43707             //    this.end();
43708             //    //console.log('adding end');
43709             }
43710             
43711         }
43712         return ret;
43713     },
43714     
43715     /**
43716      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
43717      * option "monitorValid"
43718      */
43719     startMonitoring : function(){
43720         if(!this.bound){
43721             this.bound = true;
43722             Roo.TaskMgr.start({
43723                 run : this.bindHandler,
43724                 interval : this.monitorPoll || 200,
43725                 scope: this
43726             });
43727         }
43728     },
43729
43730     /**
43731      * Stops monitoring of the valid state of this form
43732      */
43733     stopMonitoring : function(){
43734         this.bound = false;
43735     },
43736
43737     // private
43738     bindHandler : function(){
43739         if(!this.bound){
43740             return false; // stops binding
43741         }
43742         var valid = true;
43743         this.items.each(function(f){
43744             if(!f.isValid(true)){
43745                 valid = false;
43746                 return false;
43747             }
43748         });
43749         for(var i = 0, len = this.buttons.length; i < len; i++){
43750             var btn = this.buttons[i];
43751             if(btn.formBind === true && btn.disabled === valid){
43752                 btn.setDisabled(!valid);
43753             }
43754         }
43755         this.fireEvent('clientvalidation', this, valid);
43756     }
43757     
43758     
43759     
43760     
43761     
43762     
43763     
43764     
43765 });
43766
43767
43768 // back compat
43769 Roo.Form = Roo.form.Form;
43770 /*
43771  * Based on:
43772  * Ext JS Library 1.1.1
43773  * Copyright(c) 2006-2007, Ext JS, LLC.
43774  *
43775  * Originally Released Under LGPL - original licence link has changed is not relivant.
43776  *
43777  * Fork - LGPL
43778  * <script type="text/javascript">
43779  */
43780  
43781  /**
43782  * @class Roo.form.Action
43783  * Internal Class used to handle form actions
43784  * @constructor
43785  * @param {Roo.form.BasicForm} el The form element or its id
43786  * @param {Object} config Configuration options
43787  */
43788  
43789  
43790 // define the action interface
43791 Roo.form.Action = function(form, options){
43792     this.form = form;
43793     this.options = options || {};
43794 };
43795 /**
43796  * Client Validation Failed
43797  * @const 
43798  */
43799 Roo.form.Action.CLIENT_INVALID = 'client';
43800 /**
43801  * Server Validation Failed
43802  * @const 
43803  */
43804  Roo.form.Action.SERVER_INVALID = 'server';
43805  /**
43806  * Connect to Server Failed
43807  * @const 
43808  */
43809 Roo.form.Action.CONNECT_FAILURE = 'connect';
43810 /**
43811  * Reading Data from Server Failed
43812  * @const 
43813  */
43814 Roo.form.Action.LOAD_FAILURE = 'load';
43815
43816 Roo.form.Action.prototype = {
43817     type : 'default',
43818     failureType : undefined,
43819     response : undefined,
43820     result : undefined,
43821
43822     // interface method
43823     run : function(options){
43824
43825     },
43826
43827     // interface method
43828     success : function(response){
43829
43830     },
43831
43832     // interface method
43833     handleResponse : function(response){
43834
43835     },
43836
43837     // default connection failure
43838     failure : function(response){
43839         
43840         this.response = response;
43841         this.failureType = Roo.form.Action.CONNECT_FAILURE;
43842         this.form.afterAction(this, false);
43843     },
43844
43845     processResponse : function(response){
43846         this.response = response;
43847         if(!response.responseText){
43848             return true;
43849         }
43850         this.result = this.handleResponse(response);
43851         return this.result;
43852     },
43853
43854     // utility functions used internally
43855     getUrl : function(appendParams){
43856         var url = this.options.url || this.form.url || this.form.el.dom.action;
43857         if(appendParams){
43858             var p = this.getParams();
43859             if(p){
43860                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
43861             }
43862         }
43863         return url;
43864     },
43865
43866     getMethod : function(){
43867         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
43868     },
43869
43870     getParams : function(){
43871         var bp = this.form.baseParams;
43872         var p = this.options.params;
43873         if(p){
43874             if(typeof p == "object"){
43875                 p = Roo.urlEncode(Roo.applyIf(p, bp));
43876             }else if(typeof p == 'string' && bp){
43877                 p += '&' + Roo.urlEncode(bp);
43878             }
43879         }else if(bp){
43880             p = Roo.urlEncode(bp);
43881         }
43882         return p;
43883     },
43884
43885     createCallback : function(){
43886         return {
43887             success: this.success,
43888             failure: this.failure,
43889             scope: this,
43890             timeout: (this.form.timeout*1000),
43891             upload: this.form.fileUpload ? this.success : undefined
43892         };
43893     }
43894 };
43895
43896 Roo.form.Action.Submit = function(form, options){
43897     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
43898 };
43899
43900 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
43901     type : 'submit',
43902
43903     haveProgress : false,
43904     uploadComplete : false,
43905     
43906     // uploadProgress indicator.
43907     uploadProgress : function()
43908     {
43909         if (!this.form.progressUrl) {
43910             return;
43911         }
43912         
43913         if (!this.haveProgress) {
43914             Roo.MessageBox.progress("Uploading", "Uploading");
43915         }
43916         if (this.uploadComplete) {
43917            Roo.MessageBox.hide();
43918            return;
43919         }
43920         
43921         this.haveProgress = true;
43922    
43923         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
43924         
43925         var c = new Roo.data.Connection();
43926         c.request({
43927             url : this.form.progressUrl,
43928             params: {
43929                 id : uid
43930             },
43931             method: 'GET',
43932             success : function(req){
43933                //console.log(data);
43934                 var rdata = false;
43935                 var edata;
43936                 try  {
43937                    rdata = Roo.decode(req.responseText)
43938                 } catch (e) {
43939                     Roo.log("Invalid data from server..");
43940                     Roo.log(edata);
43941                     return;
43942                 }
43943                 if (!rdata || !rdata.success) {
43944                     Roo.log(rdata);
43945                     Roo.MessageBox.alert(Roo.encode(rdata));
43946                     return;
43947                 }
43948                 var data = rdata.data;
43949                 
43950                 if (this.uploadComplete) {
43951                    Roo.MessageBox.hide();
43952                    return;
43953                 }
43954                    
43955                 if (data){
43956                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
43957                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
43958                     );
43959                 }
43960                 this.uploadProgress.defer(2000,this);
43961             },
43962        
43963             failure: function(data) {
43964                 Roo.log('progress url failed ');
43965                 Roo.log(data);
43966             },
43967             scope : this
43968         });
43969            
43970     },
43971     
43972     
43973     run : function()
43974     {
43975         // run get Values on the form, so it syncs any secondary forms.
43976         this.form.getValues();
43977         
43978         var o = this.options;
43979         var method = this.getMethod();
43980         var isPost = method == 'POST';
43981         if(o.clientValidation === false || this.form.isValid()){
43982             
43983             if (this.form.progressUrl) {
43984                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
43985                     (new Date() * 1) + '' + Math.random());
43986                     
43987             } 
43988             
43989             
43990             Roo.Ajax.request(Roo.apply(this.createCallback(), {
43991                 form:this.form.el.dom,
43992                 url:this.getUrl(!isPost),
43993                 method: method,
43994                 params:isPost ? this.getParams() : null,
43995                 isUpload: this.form.fileUpload
43996             }));
43997             
43998             this.uploadProgress();
43999
44000         }else if (o.clientValidation !== false){ // client validation failed
44001             this.failureType = Roo.form.Action.CLIENT_INVALID;
44002             this.form.afterAction(this, false);
44003         }
44004     },
44005
44006     success : function(response)
44007     {
44008         this.uploadComplete= true;
44009         if (this.haveProgress) {
44010             Roo.MessageBox.hide();
44011         }
44012         
44013         
44014         var result = this.processResponse(response);
44015         if(result === true || result.success){
44016             this.form.afterAction(this, true);
44017             return;
44018         }
44019         if(result.errors){
44020             this.form.markInvalid(result.errors);
44021             this.failureType = Roo.form.Action.SERVER_INVALID;
44022         }
44023         this.form.afterAction(this, false);
44024     },
44025     failure : function(response)
44026     {
44027         this.uploadComplete= true;
44028         if (this.haveProgress) {
44029             Roo.MessageBox.hide();
44030         }
44031         
44032         this.response = response;
44033         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44034         this.form.afterAction(this, false);
44035     },
44036     
44037     handleResponse : function(response){
44038         if(this.form.errorReader){
44039             var rs = this.form.errorReader.read(response);
44040             var errors = [];
44041             if(rs.records){
44042                 for(var i = 0, len = rs.records.length; i < len; i++) {
44043                     var r = rs.records[i];
44044                     errors[i] = r.data;
44045                 }
44046             }
44047             if(errors.length < 1){
44048                 errors = null;
44049             }
44050             return {
44051                 success : rs.success,
44052                 errors : errors
44053             };
44054         }
44055         var ret = false;
44056         try {
44057             ret = Roo.decode(response.responseText);
44058         } catch (e) {
44059             ret = {
44060                 success: false,
44061                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
44062                 errors : []
44063             };
44064         }
44065         return ret;
44066         
44067     }
44068 });
44069
44070
44071 Roo.form.Action.Load = function(form, options){
44072     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
44073     this.reader = this.form.reader;
44074 };
44075
44076 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
44077     type : 'load',
44078
44079     run : function(){
44080         
44081         Roo.Ajax.request(Roo.apply(
44082                 this.createCallback(), {
44083                     method:this.getMethod(),
44084                     url:this.getUrl(false),
44085                     params:this.getParams()
44086         }));
44087     },
44088
44089     success : function(response){
44090         
44091         var result = this.processResponse(response);
44092         if(result === true || !result.success || !result.data){
44093             this.failureType = Roo.form.Action.LOAD_FAILURE;
44094             this.form.afterAction(this, false);
44095             return;
44096         }
44097         this.form.clearInvalid();
44098         this.form.setValues(result.data);
44099         this.form.afterAction(this, true);
44100     },
44101
44102     handleResponse : function(response){
44103         if(this.form.reader){
44104             var rs = this.form.reader.read(response);
44105             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
44106             return {
44107                 success : rs.success,
44108                 data : data
44109             };
44110         }
44111         return Roo.decode(response.responseText);
44112     }
44113 });
44114
44115 Roo.form.Action.ACTION_TYPES = {
44116     'load' : Roo.form.Action.Load,
44117     'submit' : Roo.form.Action.Submit
44118 };/*
44119  * Based on:
44120  * Ext JS Library 1.1.1
44121  * Copyright(c) 2006-2007, Ext JS, LLC.
44122  *
44123  * Originally Released Under LGPL - original licence link has changed is not relivant.
44124  *
44125  * Fork - LGPL
44126  * <script type="text/javascript">
44127  */
44128  
44129 /**
44130  * @class Roo.form.Layout
44131  * @extends Roo.Component
44132  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
44133  * @constructor
44134  * @param {Object} config Configuration options
44135  */
44136 Roo.form.Layout = function(config){
44137     var xitems = [];
44138     if (config.items) {
44139         xitems = config.items;
44140         delete config.items;
44141     }
44142     Roo.form.Layout.superclass.constructor.call(this, config);
44143     this.stack = [];
44144     Roo.each(xitems, this.addxtype, this);
44145      
44146 };
44147
44148 Roo.extend(Roo.form.Layout, Roo.Component, {
44149     /**
44150      * @cfg {String/Object} autoCreate
44151      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
44152      */
44153     /**
44154      * @cfg {String/Object/Function} style
44155      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
44156      * a function which returns such a specification.
44157      */
44158     /**
44159      * @cfg {String} labelAlign
44160      * Valid values are "left," "top" and "right" (defaults to "left")
44161      */
44162     /**
44163      * @cfg {Number} labelWidth
44164      * Fixed width in pixels of all field labels (defaults to undefined)
44165      */
44166     /**
44167      * @cfg {Boolean} clear
44168      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
44169      */
44170     clear : true,
44171     /**
44172      * @cfg {String} labelSeparator
44173      * The separator to use after field labels (defaults to ':')
44174      */
44175     labelSeparator : ':',
44176     /**
44177      * @cfg {Boolean} hideLabels
44178      * True to suppress the display of field labels in this layout (defaults to false)
44179      */
44180     hideLabels : false,
44181
44182     // private
44183     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
44184     
44185     isLayout : true,
44186     
44187     // private
44188     onRender : function(ct, position){
44189         if(this.el){ // from markup
44190             this.el = Roo.get(this.el);
44191         }else {  // generate
44192             var cfg = this.getAutoCreate();
44193             this.el = ct.createChild(cfg, position);
44194         }
44195         if(this.style){
44196             this.el.applyStyles(this.style);
44197         }
44198         if(this.labelAlign){
44199             this.el.addClass('x-form-label-'+this.labelAlign);
44200         }
44201         if(this.hideLabels){
44202             this.labelStyle = "display:none";
44203             this.elementStyle = "padding-left:0;";
44204         }else{
44205             if(typeof this.labelWidth == 'number'){
44206                 this.labelStyle = "width:"+this.labelWidth+"px;";
44207                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
44208             }
44209             if(this.labelAlign == 'top'){
44210                 this.labelStyle = "width:auto;";
44211                 this.elementStyle = "padding-left:0;";
44212             }
44213         }
44214         var stack = this.stack;
44215         var slen = stack.length;
44216         if(slen > 0){
44217             if(!this.fieldTpl){
44218                 var t = new Roo.Template(
44219                     '<div class="x-form-item {5}">',
44220                         '<label for="{0}" style="{2}">{1}{4}</label>',
44221                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44222                         '</div>',
44223                     '</div><div class="x-form-clear-left"></div>'
44224                 );
44225                 t.disableFormats = true;
44226                 t.compile();
44227                 Roo.form.Layout.prototype.fieldTpl = t;
44228             }
44229             for(var i = 0; i < slen; i++) {
44230                 if(stack[i].isFormField){
44231                     this.renderField(stack[i]);
44232                 }else{
44233                     this.renderComponent(stack[i]);
44234                 }
44235             }
44236         }
44237         if(this.clear){
44238             this.el.createChild({cls:'x-form-clear'});
44239         }
44240     },
44241
44242     // private
44243     renderField : function(f){
44244         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
44245                f.id, //0
44246                f.fieldLabel, //1
44247                f.labelStyle||this.labelStyle||'', //2
44248                this.elementStyle||'', //3
44249                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
44250                f.itemCls||this.itemCls||''  //5
44251        ], true).getPrevSibling());
44252     },
44253
44254     // private
44255     renderComponent : function(c){
44256         c.render(c.isLayout ? this.el : this.el.createChild());    
44257     },
44258     /**
44259      * Adds a object form elements (using the xtype property as the factory method.)
44260      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
44261      * @param {Object} config 
44262      */
44263     addxtype : function(o)
44264     {
44265         // create the lement.
44266         o.form = this.form;
44267         var fe = Roo.factory(o, Roo.form);
44268         this.form.allItems.push(fe);
44269         this.stack.push(fe);
44270         
44271         if (fe.isFormField) {
44272             this.form.items.add(fe);
44273         }
44274          
44275         return fe;
44276     }
44277 });
44278
44279 /**
44280  * @class Roo.form.Column
44281  * @extends Roo.form.Layout
44282  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
44283  * @constructor
44284  * @param {Object} config Configuration options
44285  */
44286 Roo.form.Column = function(config){
44287     Roo.form.Column.superclass.constructor.call(this, config);
44288 };
44289
44290 Roo.extend(Roo.form.Column, Roo.form.Layout, {
44291     /**
44292      * @cfg {Number/String} width
44293      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44294      */
44295     /**
44296      * @cfg {String/Object} autoCreate
44297      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
44298      */
44299
44300     // private
44301     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
44302
44303     // private
44304     onRender : function(ct, position){
44305         Roo.form.Column.superclass.onRender.call(this, ct, position);
44306         if(this.width){
44307             this.el.setWidth(this.width);
44308         }
44309     }
44310 });
44311
44312
44313 /**
44314  * @class Roo.form.Row
44315  * @extends Roo.form.Layout
44316  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
44317  * @constructor
44318  * @param {Object} config Configuration options
44319  */
44320
44321  
44322 Roo.form.Row = function(config){
44323     Roo.form.Row.superclass.constructor.call(this, config);
44324 };
44325  
44326 Roo.extend(Roo.form.Row, Roo.form.Layout, {
44327       /**
44328      * @cfg {Number/String} width
44329      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44330      */
44331     /**
44332      * @cfg {Number/String} height
44333      * The fixed height of the column in pixels or CSS value (defaults to "auto")
44334      */
44335     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
44336     
44337     padWidth : 20,
44338     // private
44339     onRender : function(ct, position){
44340         //console.log('row render');
44341         if(!this.rowTpl){
44342             var t = new Roo.Template(
44343                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
44344                     '<label for="{0}" style="{2}">{1}{4}</label>',
44345                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44346                     '</div>',
44347                 '</div>'
44348             );
44349             t.disableFormats = true;
44350             t.compile();
44351             Roo.form.Layout.prototype.rowTpl = t;
44352         }
44353         this.fieldTpl = this.rowTpl;
44354         
44355         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
44356         var labelWidth = 100;
44357         
44358         if ((this.labelAlign != 'top')) {
44359             if (typeof this.labelWidth == 'number') {
44360                 labelWidth = this.labelWidth
44361             }
44362             this.padWidth =  20 + labelWidth;
44363             
44364         }
44365         
44366         Roo.form.Column.superclass.onRender.call(this, ct, position);
44367         if(this.width){
44368             this.el.setWidth(this.width);
44369         }
44370         if(this.height){
44371             this.el.setHeight(this.height);
44372         }
44373     },
44374     
44375     // private
44376     renderField : function(f){
44377         f.fieldEl = this.fieldTpl.append(this.el, [
44378                f.id, f.fieldLabel,
44379                f.labelStyle||this.labelStyle||'',
44380                this.elementStyle||'',
44381                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
44382                f.itemCls||this.itemCls||'',
44383                f.width ? f.width + this.padWidth : 160 + this.padWidth
44384        ],true);
44385     }
44386 });
44387  
44388
44389 /**
44390  * @class Roo.form.FieldSet
44391  * @extends Roo.form.Layout
44392  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
44393  * @constructor
44394  * @param {Object} config Configuration options
44395  */
44396 Roo.form.FieldSet = function(config){
44397     Roo.form.FieldSet.superclass.constructor.call(this, config);
44398 };
44399
44400 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
44401     /**
44402      * @cfg {String} legend
44403      * The text to display as the legend for the FieldSet (defaults to '')
44404      */
44405     /**
44406      * @cfg {String/Object} autoCreate
44407      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
44408      */
44409
44410     // private
44411     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
44412
44413     // private
44414     onRender : function(ct, position){
44415         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
44416         if(this.legend){
44417             this.setLegend(this.legend);
44418         }
44419     },
44420
44421     // private
44422     setLegend : function(text){
44423         if(this.rendered){
44424             this.el.child('legend').update(text);
44425         }
44426     }
44427 });/*
44428  * Based on:
44429  * Ext JS Library 1.1.1
44430  * Copyright(c) 2006-2007, Ext JS, LLC.
44431  *
44432  * Originally Released Under LGPL - original licence link has changed is not relivant.
44433  *
44434  * Fork - LGPL
44435  * <script type="text/javascript">
44436  */
44437 /**
44438  * @class Roo.form.VTypes
44439  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
44440  * @singleton
44441  */
44442 Roo.form.VTypes = function(){
44443     // closure these in so they are only created once.
44444     var alpha = /^[a-zA-Z_]+$/;
44445     var alphanum = /^[a-zA-Z0-9_]+$/;
44446     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
44447     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
44448
44449     // All these messages and functions are configurable
44450     return {
44451         /**
44452          * The function used to validate email addresses
44453          * @param {String} value The email address
44454          */
44455         'email' : function(v){
44456             return email.test(v);
44457         },
44458         /**
44459          * The error text to display when the email validation function returns false
44460          * @type String
44461          */
44462         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
44463         /**
44464          * The keystroke filter mask to be applied on email input
44465          * @type RegExp
44466          */
44467         'emailMask' : /[a-z0-9_\.\-@]/i,
44468
44469         /**
44470          * The function used to validate URLs
44471          * @param {String} value The URL
44472          */
44473         'url' : function(v){
44474             return url.test(v);
44475         },
44476         /**
44477          * The error text to display when the url validation function returns false
44478          * @type String
44479          */
44480         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
44481         
44482         /**
44483          * The function used to validate alpha values
44484          * @param {String} value The value
44485          */
44486         'alpha' : function(v){
44487             return alpha.test(v);
44488         },
44489         /**
44490          * The error text to display when the alpha validation function returns false
44491          * @type String
44492          */
44493         'alphaText' : 'This field should only contain letters and _',
44494         /**
44495          * The keystroke filter mask to be applied on alpha input
44496          * @type RegExp
44497          */
44498         'alphaMask' : /[a-z_]/i,
44499
44500         /**
44501          * The function used to validate alphanumeric values
44502          * @param {String} value The value
44503          */
44504         'alphanum' : function(v){
44505             return alphanum.test(v);
44506         },
44507         /**
44508          * The error text to display when the alphanumeric validation function returns false
44509          * @type String
44510          */
44511         'alphanumText' : 'This field should only contain letters, numbers and _',
44512         /**
44513          * The keystroke filter mask to be applied on alphanumeric input
44514          * @type RegExp
44515          */
44516         'alphanumMask' : /[a-z0-9_]/i
44517     };
44518 }();//<script type="text/javascript">
44519
44520 /**
44521  * @class Roo.form.FCKeditor
44522  * @extends Roo.form.TextArea
44523  * Wrapper around the FCKEditor http://www.fckeditor.net
44524  * @constructor
44525  * Creates a new FCKeditor
44526  * @param {Object} config Configuration options
44527  */
44528 Roo.form.FCKeditor = function(config){
44529     Roo.form.FCKeditor.superclass.constructor.call(this, config);
44530     this.addEvents({
44531          /**
44532          * @event editorinit
44533          * Fired when the editor is initialized - you can add extra handlers here..
44534          * @param {FCKeditor} this
44535          * @param {Object} the FCK object.
44536          */
44537         editorinit : true
44538     });
44539     
44540     
44541 };
44542 Roo.form.FCKeditor.editors = { };
44543 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
44544 {
44545     //defaultAutoCreate : {
44546     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
44547     //},
44548     // private
44549     /**
44550      * @cfg {Object} fck options - see fck manual for details.
44551      */
44552     fckconfig : false,
44553     
44554     /**
44555      * @cfg {Object} fck toolbar set (Basic or Default)
44556      */
44557     toolbarSet : 'Basic',
44558     /**
44559      * @cfg {Object} fck BasePath
44560      */ 
44561     basePath : '/fckeditor/',
44562     
44563     
44564     frame : false,
44565     
44566     value : '',
44567     
44568    
44569     onRender : function(ct, position)
44570     {
44571         if(!this.el){
44572             this.defaultAutoCreate = {
44573                 tag: "textarea",
44574                 style:"width:300px;height:60px;",
44575                 autocomplete: "off"
44576             };
44577         }
44578         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
44579         /*
44580         if(this.grow){
44581             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
44582             if(this.preventScrollbars){
44583                 this.el.setStyle("overflow", "hidden");
44584             }
44585             this.el.setHeight(this.growMin);
44586         }
44587         */
44588         //console.log('onrender' + this.getId() );
44589         Roo.form.FCKeditor.editors[this.getId()] = this;
44590          
44591
44592         this.replaceTextarea() ;
44593         
44594     },
44595     
44596     getEditor : function() {
44597         return this.fckEditor;
44598     },
44599     /**
44600      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
44601      * @param {Mixed} value The value to set
44602      */
44603     
44604     
44605     setValue : function(value)
44606     {
44607         //console.log('setValue: ' + value);
44608         
44609         if(typeof(value) == 'undefined') { // not sure why this is happending...
44610             return;
44611         }
44612         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44613         
44614         //if(!this.el || !this.getEditor()) {
44615         //    this.value = value;
44616             //this.setValue.defer(100,this,[value]);    
44617         //    return;
44618         //} 
44619         
44620         if(!this.getEditor()) {
44621             return;
44622         }
44623         
44624         this.getEditor().SetData(value);
44625         
44626         //
44627
44628     },
44629
44630     /**
44631      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
44632      * @return {Mixed} value The field value
44633      */
44634     getValue : function()
44635     {
44636         
44637         if (this.frame && this.frame.dom.style.display == 'none') {
44638             return Roo.form.FCKeditor.superclass.getValue.call(this);
44639         }
44640         
44641         if(!this.el || !this.getEditor()) {
44642            
44643            // this.getValue.defer(100,this); 
44644             return this.value;
44645         }
44646        
44647         
44648         var value=this.getEditor().GetData();
44649         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44650         return Roo.form.FCKeditor.superclass.getValue.call(this);
44651         
44652
44653     },
44654
44655     /**
44656      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
44657      * @return {Mixed} value The field value
44658      */
44659     getRawValue : function()
44660     {
44661         if (this.frame && this.frame.dom.style.display == 'none') {
44662             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44663         }
44664         
44665         if(!this.el || !this.getEditor()) {
44666             //this.getRawValue.defer(100,this); 
44667             return this.value;
44668             return;
44669         }
44670         
44671         
44672         
44673         var value=this.getEditor().GetData();
44674         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
44675         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44676          
44677     },
44678     
44679     setSize : function(w,h) {
44680         
44681         
44682         
44683         //if (this.frame && this.frame.dom.style.display == 'none') {
44684         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44685         //    return;
44686         //}
44687         //if(!this.el || !this.getEditor()) {
44688         //    this.setSize.defer(100,this, [w,h]); 
44689         //    return;
44690         //}
44691         
44692         
44693         
44694         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44695         
44696         this.frame.dom.setAttribute('width', w);
44697         this.frame.dom.setAttribute('height', h);
44698         this.frame.setSize(w,h);
44699         
44700     },
44701     
44702     toggleSourceEdit : function(value) {
44703         
44704       
44705          
44706         this.el.dom.style.display = value ? '' : 'none';
44707         this.frame.dom.style.display = value ?  'none' : '';
44708         
44709     },
44710     
44711     
44712     focus: function(tag)
44713     {
44714         if (this.frame.dom.style.display == 'none') {
44715             return Roo.form.FCKeditor.superclass.focus.call(this);
44716         }
44717         if(!this.el || !this.getEditor()) {
44718             this.focus.defer(100,this, [tag]); 
44719             return;
44720         }
44721         
44722         
44723         
44724         
44725         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
44726         this.getEditor().Focus();
44727         if (tgs.length) {
44728             if (!this.getEditor().Selection.GetSelection()) {
44729                 this.focus.defer(100,this, [tag]); 
44730                 return;
44731             }
44732             
44733             
44734             var r = this.getEditor().EditorDocument.createRange();
44735             r.setStart(tgs[0],0);
44736             r.setEnd(tgs[0],0);
44737             this.getEditor().Selection.GetSelection().removeAllRanges();
44738             this.getEditor().Selection.GetSelection().addRange(r);
44739             this.getEditor().Focus();
44740         }
44741         
44742     },
44743     
44744     
44745     
44746     replaceTextarea : function()
44747     {
44748         if ( document.getElementById( this.getId() + '___Frame' ) )
44749             return ;
44750         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
44751         //{
44752             // We must check the elements firstly using the Id and then the name.
44753         var oTextarea = document.getElementById( this.getId() );
44754         
44755         var colElementsByName = document.getElementsByName( this.getId() ) ;
44756          
44757         oTextarea.style.display = 'none' ;
44758
44759         if ( oTextarea.tabIndex ) {            
44760             this.TabIndex = oTextarea.tabIndex ;
44761         }
44762         
44763         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
44764         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
44765         this.frame = Roo.get(this.getId() + '___Frame')
44766     },
44767     
44768     _getConfigHtml : function()
44769     {
44770         var sConfig = '' ;
44771
44772         for ( var o in this.fckconfig ) {
44773             sConfig += sConfig.length > 0  ? '&amp;' : '';
44774             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
44775         }
44776
44777         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
44778     },
44779     
44780     
44781     _getIFrameHtml : function()
44782     {
44783         var sFile = 'fckeditor.html' ;
44784         /* no idea what this is about..
44785         try
44786         {
44787             if ( (/fcksource=true/i).test( window.top.location.search ) )
44788                 sFile = 'fckeditor.original.html' ;
44789         }
44790         catch (e) { 
44791         */
44792
44793         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
44794         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
44795         
44796         
44797         var html = '<iframe id="' + this.getId() +
44798             '___Frame" src="' + sLink +
44799             '" width="' + this.width +
44800             '" height="' + this.height + '"' +
44801             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
44802             ' frameborder="0" scrolling="no"></iframe>' ;
44803
44804         return html ;
44805     },
44806     
44807     _insertHtmlBefore : function( html, element )
44808     {
44809         if ( element.insertAdjacentHTML )       {
44810             // IE
44811             element.insertAdjacentHTML( 'beforeBegin', html ) ;
44812         } else { // Gecko
44813             var oRange = document.createRange() ;
44814             oRange.setStartBefore( element ) ;
44815             var oFragment = oRange.createContextualFragment( html );
44816             element.parentNode.insertBefore( oFragment, element ) ;
44817         }
44818     }
44819     
44820     
44821   
44822     
44823     
44824     
44825     
44826
44827 });
44828
44829 //Roo.reg('fckeditor', Roo.form.FCKeditor);
44830
44831 function FCKeditor_OnComplete(editorInstance){
44832     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
44833     f.fckEditor = editorInstance;
44834     //console.log("loaded");
44835     f.fireEvent('editorinit', f, editorInstance);
44836
44837   
44838
44839  
44840
44841
44842
44843
44844
44845
44846
44847
44848
44849
44850
44851
44852
44853
44854
44855 //<script type="text/javascript">
44856 /**
44857  * @class Roo.form.GridField
44858  * @extends Roo.form.Field
44859  * Embed a grid (or editable grid into a form)
44860  * STATUS ALPHA
44861  * 
44862  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
44863  * it needs 
44864  * xgrid.store = Roo.data.Store
44865  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
44866  * xgrid.store.reader = Roo.data.JsonReader 
44867  * 
44868  * 
44869  * @constructor
44870  * Creates a new GridField
44871  * @param {Object} config Configuration options
44872  */
44873 Roo.form.GridField = function(config){
44874     Roo.form.GridField.superclass.constructor.call(this, config);
44875      
44876 };
44877
44878 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
44879     /**
44880      * @cfg {Number} width  - used to restrict width of grid..
44881      */
44882     width : 100,
44883     /**
44884      * @cfg {Number} height - used to restrict height of grid..
44885      */
44886     height : 50,
44887      /**
44888      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
44889          * 
44890          *}
44891      */
44892     xgrid : false, 
44893     /**
44894      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44895      * {tag: "input", type: "checkbox", autocomplete: "off"})
44896      */
44897    // defaultAutoCreate : { tag: 'div' },
44898     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
44899     /**
44900      * @cfg {String} addTitle Text to include for adding a title.
44901      */
44902     addTitle : false,
44903     //
44904     onResize : function(){
44905         Roo.form.Field.superclass.onResize.apply(this, arguments);
44906     },
44907
44908     initEvents : function(){
44909         // Roo.form.Checkbox.superclass.initEvents.call(this);
44910         // has no events...
44911        
44912     },
44913
44914
44915     getResizeEl : function(){
44916         return this.wrap;
44917     },
44918
44919     getPositionEl : function(){
44920         return this.wrap;
44921     },
44922
44923     // private
44924     onRender : function(ct, position){
44925         
44926         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
44927         var style = this.style;
44928         delete this.style;
44929         
44930         Roo.form.GridField.superclass.onRender.call(this, ct, position);
44931         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
44932         this.viewEl = this.wrap.createChild({ tag: 'div' });
44933         if (style) {
44934             this.viewEl.applyStyles(style);
44935         }
44936         if (this.width) {
44937             this.viewEl.setWidth(this.width);
44938         }
44939         if (this.height) {
44940             this.viewEl.setHeight(this.height);
44941         }
44942         //if(this.inputValue !== undefined){
44943         //this.setValue(this.value);
44944         
44945         
44946         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
44947         
44948         
44949         this.grid.render();
44950         this.grid.getDataSource().on('remove', this.refreshValue, this);
44951         this.grid.getDataSource().on('update', this.refreshValue, this);
44952         this.grid.on('afteredit', this.refreshValue, this);
44953  
44954     },
44955      
44956     
44957     /**
44958      * Sets the value of the item. 
44959      * @param {String} either an object  or a string..
44960      */
44961     setValue : function(v){
44962         //this.value = v;
44963         v = v || []; // empty set..
44964         // this does not seem smart - it really only affects memoryproxy grids..
44965         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
44966             var ds = this.grid.getDataSource();
44967             // assumes a json reader..
44968             var data = {}
44969             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
44970             ds.loadData( data);
44971         }
44972         // clear selection so it does not get stale.
44973         if (this.grid.sm) { 
44974             this.grid.sm.clearSelections();
44975         }
44976         
44977         Roo.form.GridField.superclass.setValue.call(this, v);
44978         this.refreshValue();
44979         // should load data in the grid really....
44980     },
44981     
44982     // private
44983     refreshValue: function() {
44984          var val = [];
44985         this.grid.getDataSource().each(function(r) {
44986             val.push(r.data);
44987         });
44988         this.el.dom.value = Roo.encode(val);
44989     }
44990     
44991      
44992     
44993     
44994 });/*
44995  * Based on:
44996  * Ext JS Library 1.1.1
44997  * Copyright(c) 2006-2007, Ext JS, LLC.
44998  *
44999  * Originally Released Under LGPL - original licence link has changed is not relivant.
45000  *
45001  * Fork - LGPL
45002  * <script type="text/javascript">
45003  */
45004 /**
45005  * @class Roo.form.DisplayField
45006  * @extends Roo.form.Field
45007  * A generic Field to display non-editable data.
45008  * @constructor
45009  * Creates a new Display Field item.
45010  * @param {Object} config Configuration options
45011  */
45012 Roo.form.DisplayField = function(config){
45013     Roo.form.DisplayField.superclass.constructor.call(this, config);
45014     
45015 };
45016
45017 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45018     inputType:      'hidden',
45019     allowBlank:     true,
45020     readOnly:         true,
45021     
45022  
45023     /**
45024      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45025      */
45026     focusClass : undefined,
45027     /**
45028      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45029      */
45030     fieldClass: 'x-form-field',
45031     
45032      /**
45033      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45034      */
45035     valueRenderer: undefined,
45036     
45037     width: 100,
45038     /**
45039      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45040      * {tag: "input", type: "checkbox", autocomplete: "off"})
45041      */
45042      
45043  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45044
45045     onResize : function(){
45046         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45047         
45048     },
45049
45050     initEvents : function(){
45051         // Roo.form.Checkbox.superclass.initEvents.call(this);
45052         // has no events...
45053        
45054     },
45055
45056
45057     getResizeEl : function(){
45058         return this.wrap;
45059     },
45060
45061     getPositionEl : function(){
45062         return this.wrap;
45063     },
45064
45065     // private
45066     onRender : function(ct, position){
45067         
45068         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
45069         //if(this.inputValue !== undefined){
45070         this.wrap = this.el.wrap();
45071         
45072         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
45073         
45074         if (this.bodyStyle) {
45075             this.viewEl.applyStyles(this.bodyStyle);
45076         }
45077         //this.viewEl.setStyle('padding', '2px');
45078         
45079         this.setValue(this.value);
45080         
45081     },
45082 /*
45083     // private
45084     initValue : Roo.emptyFn,
45085
45086   */
45087
45088         // private
45089     onClick : function(){
45090         
45091     },
45092
45093     /**
45094      * Sets the checked state of the checkbox.
45095      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
45096      */
45097     setValue : function(v){
45098         this.value = v;
45099         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
45100         // this might be called before we have a dom element..
45101         if (!this.viewEl) {
45102             return;
45103         }
45104         this.viewEl.dom.innerHTML = html;
45105         Roo.form.DisplayField.superclass.setValue.call(this, v);
45106
45107     }
45108 });/*
45109  * 
45110  * Licence- LGPL
45111  * 
45112  */
45113
45114 /**
45115  * @class Roo.form.DayPicker
45116  * @extends Roo.form.Field
45117  * A Day picker show [M] [T] [W] ....
45118  * @constructor
45119  * Creates a new Day Picker
45120  * @param {Object} config Configuration options
45121  */
45122 Roo.form.DayPicker= function(config){
45123     Roo.form.DayPicker.superclass.constructor.call(this, config);
45124      
45125 };
45126
45127 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
45128     /**
45129      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45130      */
45131     focusClass : undefined,
45132     /**
45133      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45134      */
45135     fieldClass: "x-form-field",
45136    
45137     /**
45138      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45139      * {tag: "input", type: "checkbox", autocomplete: "off"})
45140      */
45141     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
45142     
45143    
45144     actionMode : 'viewEl', 
45145     //
45146     // private
45147  
45148     inputType : 'hidden',
45149     
45150      
45151     inputElement: false, // real input element?
45152     basedOn: false, // ????
45153     
45154     isFormField: true, // not sure where this is needed!!!!
45155
45156     onResize : function(){
45157         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
45158         if(!this.boxLabel){
45159             this.el.alignTo(this.wrap, 'c-c');
45160         }
45161     },
45162
45163     initEvents : function(){
45164         Roo.form.Checkbox.superclass.initEvents.call(this);
45165         this.el.on("click", this.onClick,  this);
45166         this.el.on("change", this.onClick,  this);
45167     },
45168
45169
45170     getResizeEl : function(){
45171         return this.wrap;
45172     },
45173
45174     getPositionEl : function(){
45175         return this.wrap;
45176     },
45177
45178     
45179     // private
45180     onRender : function(ct, position){
45181         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45182        
45183         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
45184         
45185         var r1 = '<table><tr>';
45186         var r2 = '<tr class="x-form-daypick-icons">';
45187         for (var i=0; i < 7; i++) {
45188             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
45189             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
45190         }
45191         
45192         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
45193         viewEl.select('img').on('click', this.onClick, this);
45194         this.viewEl = viewEl;   
45195         
45196         
45197         // this will not work on Chrome!!!
45198         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
45199         this.el.on('propertychange', this.setFromHidden,  this);  //ie
45200         
45201         
45202           
45203
45204     },
45205
45206     // private
45207     initValue : Roo.emptyFn,
45208
45209     /**
45210      * Returns the checked state of the checkbox.
45211      * @return {Boolean} True if checked, else false
45212      */
45213     getValue : function(){
45214         return this.el.dom.value;
45215         
45216     },
45217
45218         // private
45219     onClick : function(e){ 
45220         //this.setChecked(!this.checked);
45221         Roo.get(e.target).toggleClass('x-menu-item-checked');
45222         this.refreshValue();
45223         //if(this.el.dom.checked != this.checked){
45224         //    this.setValue(this.el.dom.checked);
45225        // }
45226     },
45227     
45228     // private
45229     refreshValue : function()
45230     {
45231         var val = '';
45232         this.viewEl.select('img',true).each(function(e,i,n)  {
45233             val += e.is(".x-menu-item-checked") ? String(n) : '';
45234         });
45235         this.setValue(val, true);
45236     },
45237
45238     /**
45239      * Sets the checked state of the checkbox.
45240      * On is always based on a string comparison between inputValue and the param.
45241      * @param {Boolean/String} value - the value to set 
45242      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
45243      */
45244     setValue : function(v,suppressEvent){
45245         if (!this.el.dom) {
45246             return;
45247         }
45248         var old = this.el.dom.value ;
45249         this.el.dom.value = v;
45250         if (suppressEvent) {
45251             return ;
45252         }
45253          
45254         // update display..
45255         this.viewEl.select('img',true).each(function(e,i,n)  {
45256             
45257             var on = e.is(".x-menu-item-checked");
45258             var newv = v.indexOf(String(n)) > -1;
45259             if (on != newv) {
45260                 e.toggleClass('x-menu-item-checked');
45261             }
45262             
45263         });
45264         
45265         
45266         this.fireEvent('change', this, v, old);
45267         
45268         
45269     },
45270    
45271     // handle setting of hidden value by some other method!!?!?
45272     setFromHidden: function()
45273     {
45274         if(!this.el){
45275             return;
45276         }
45277         //console.log("SET FROM HIDDEN");
45278         //alert('setFrom hidden');
45279         this.setValue(this.el.dom.value);
45280     },
45281     
45282     onDestroy : function()
45283     {
45284         if(this.viewEl){
45285             Roo.get(this.viewEl).remove();
45286         }
45287          
45288         Roo.form.DayPicker.superclass.onDestroy.call(this);
45289     }
45290
45291 });/*
45292  * RooJS Library 1.1.1
45293  * Copyright(c) 2008-2011  Alan Knowles
45294  *
45295  * License - LGPL
45296  */
45297  
45298
45299 /**
45300  * @class Roo.form.ComboCheck
45301  * @extends Roo.form.ComboBox
45302  * A combobox for multiple select items.
45303  *
45304  * FIXME - could do with a reset button..
45305  * 
45306  * @constructor
45307  * Create a new ComboCheck
45308  * @param {Object} config Configuration options
45309  */
45310 Roo.form.ComboCheck = function(config){
45311     Roo.form.ComboCheck.superclass.constructor.call(this, config);
45312     // should verify some data...
45313     // like
45314     // hiddenName = required..
45315     // displayField = required
45316     // valudField == required
45317     var req= [ 'hiddenName', 'displayField', 'valueField' ];
45318     var _t = this;
45319     Roo.each(req, function(e) {
45320         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
45321             throw "Roo.form.ComboCheck : missing value for: " + e;
45322         }
45323     });
45324     
45325     
45326 };
45327
45328 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
45329      
45330      
45331     editable : false,
45332      
45333     selectedClass: 'x-menu-item-checked', 
45334     
45335     // private
45336     onRender : function(ct, position){
45337         var _t = this;
45338         
45339         
45340         
45341         if(!this.tpl){
45342             var cls = 'x-combo-list';
45343
45344             
45345             this.tpl =  new Roo.Template({
45346                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
45347                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
45348                    '<span>{' + this.displayField + '}</span>' +
45349                     '</div>' 
45350                 
45351             });
45352         }
45353  
45354         
45355         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
45356         this.view.singleSelect = false;
45357         this.view.multiSelect = true;
45358         this.view.toggleSelect = true;
45359         this.pageTb.add(new Roo.Toolbar.Fill(), {
45360             
45361             text: 'Done',
45362             handler: function()
45363             {
45364                 _t.collapse();
45365             }
45366         });
45367     },
45368     
45369     onViewOver : function(e, t){
45370         // do nothing...
45371         return;
45372         
45373     },
45374     
45375     onViewClick : function(doFocus,index){
45376         return;
45377         
45378     },
45379     select: function () {
45380         //Roo.log("SELECT CALLED");
45381     },
45382      
45383     selectByValue : function(xv, scrollIntoView){
45384         var ar = this.getValueArray();
45385         var sels = [];
45386         
45387         Roo.each(ar, function(v) {
45388             if(v === undefined || v === null){
45389                 return;
45390             }
45391             var r = this.findRecord(this.valueField, v);
45392             if(r){
45393                 sels.push(this.store.indexOf(r))
45394                 
45395             }
45396         },this);
45397         this.view.select(sels);
45398         return false;
45399     },
45400     
45401     
45402     
45403     onSelect : function(record, index){
45404        // Roo.log("onselect Called");
45405        // this is only called by the clear button now..
45406         this.view.clearSelections();
45407         this.setValue('[]');
45408         if (this.value != this.valueBefore) {
45409             this.fireEvent('change', this, this.value, this.valueBefore);
45410         }
45411     },
45412     getValueArray : function()
45413     {
45414         var ar = [] ;
45415         
45416         try {
45417             //Roo.log(this.value);
45418             if (typeof(this.value) == 'undefined') {
45419                 return [];
45420             }
45421             var ar = Roo.decode(this.value);
45422             return  ar instanceof Array ? ar : []; //?? valid?
45423             
45424         } catch(e) {
45425             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
45426             return [];
45427         }
45428          
45429     },
45430     expand : function ()
45431     {
45432         Roo.form.ComboCheck.superclass.expand.call(this);
45433         this.valueBefore = this.value;
45434         
45435
45436     },
45437     
45438     collapse : function(){
45439         Roo.form.ComboCheck.superclass.collapse.call(this);
45440         var sl = this.view.getSelectedIndexes();
45441         var st = this.store;
45442         var nv = [];
45443         var tv = [];
45444         var r;
45445         Roo.each(sl, function(i) {
45446             r = st.getAt(i);
45447             nv.push(r.get(this.valueField));
45448         },this);
45449         this.setValue(Roo.encode(nv));
45450         if (this.value != this.valueBefore) {
45451
45452             this.fireEvent('change', this, this.value, this.valueBefore);
45453         }
45454         
45455     },
45456     
45457     setValue : function(v){
45458         // Roo.log(v);
45459         this.value = v;
45460         
45461         var vals = this.getValueArray();
45462         var tv = [];
45463         Roo.each(vals, function(k) {
45464             var r = this.findRecord(this.valueField, k);
45465             if(r){
45466                 tv.push(r.data[this.displayField]);
45467             }else if(this.valueNotFoundText !== undefined){
45468                 tv.push( this.valueNotFoundText );
45469             }
45470         },this);
45471        // Roo.log(tv);
45472         
45473         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
45474         this.hiddenField.value = v;
45475         this.value = v;
45476     }
45477     
45478 });//<script type="text/javasscript">
45479  
45480
45481 /**
45482  * @class Roo.DDView
45483  * A DnD enabled version of Roo.View.
45484  * @param {Element/String} container The Element in which to create the View.
45485  * @param {String} tpl The template string used to create the markup for each element of the View
45486  * @param {Object} config The configuration properties. These include all the config options of
45487  * {@link Roo.View} plus some specific to this class.<br>
45488  * <p>
45489  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
45490  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
45491  * <p>
45492  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
45493 .x-view-drag-insert-above {
45494         border-top:1px dotted #3366cc;
45495 }
45496 .x-view-drag-insert-below {
45497         border-bottom:1px dotted #3366cc;
45498 }
45499 </code></pre>
45500  * 
45501  */
45502  
45503 Roo.DDView = function(container, tpl, config) {
45504     Roo.DDView.superclass.constructor.apply(this, arguments);
45505     this.getEl().setStyle("outline", "0px none");
45506     this.getEl().unselectable();
45507     if (this.dragGroup) {
45508                 this.setDraggable(this.dragGroup.split(","));
45509     }
45510     if (this.dropGroup) {
45511                 this.setDroppable(this.dropGroup.split(","));
45512     }
45513     if (this.deletable) {
45514         this.setDeletable();
45515     }
45516     this.isDirtyFlag = false;
45517         this.addEvents({
45518                 "drop" : true
45519         });
45520 };
45521
45522 Roo.extend(Roo.DDView, Roo.View, {
45523 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
45524 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
45525 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
45526 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
45527
45528         isFormField: true,
45529
45530         reset: Roo.emptyFn,
45531         
45532         clearInvalid: Roo.form.Field.prototype.clearInvalid,
45533
45534         validate: function() {
45535                 return true;
45536         },
45537         
45538         destroy: function() {
45539                 this.purgeListeners();
45540                 this.getEl.removeAllListeners();
45541                 this.getEl().remove();
45542                 if (this.dragZone) {
45543                         if (this.dragZone.destroy) {
45544                                 this.dragZone.destroy();
45545                         }
45546                 }
45547                 if (this.dropZone) {
45548                         if (this.dropZone.destroy) {
45549                                 this.dropZone.destroy();
45550                         }
45551                 }
45552         },
45553
45554 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
45555         getName: function() {
45556                 return this.name;
45557         },
45558
45559 /**     Loads the View from a JSON string representing the Records to put into the Store. */
45560         setValue: function(v) {
45561                 if (!this.store) {
45562                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
45563                 }
45564                 var data = {};
45565                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
45566                 this.store.proxy = new Roo.data.MemoryProxy(data);
45567                 this.store.load();
45568         },
45569
45570 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
45571         getValue: function() {
45572                 var result = '(';
45573                 this.store.each(function(rec) {
45574                         result += rec.id + ',';
45575                 });
45576                 return result.substr(0, result.length - 1) + ')';
45577         },
45578         
45579         getIds: function() {
45580                 var i = 0, result = new Array(this.store.getCount());
45581                 this.store.each(function(rec) {
45582                         result[i++] = rec.id;
45583                 });
45584                 return result;
45585         },
45586         
45587         isDirty: function() {
45588                 return this.isDirtyFlag;
45589         },
45590
45591 /**
45592  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
45593  *      whole Element becomes the target, and this causes the drop gesture to append.
45594  */
45595     getTargetFromEvent : function(e) {
45596                 var target = e.getTarget();
45597                 while ((target !== null) && (target.parentNode != this.el.dom)) {
45598                 target = target.parentNode;
45599                 }
45600                 if (!target) {
45601                         target = this.el.dom.lastChild || this.el.dom;
45602                 }
45603                 return target;
45604     },
45605
45606 /**
45607  *      Create the drag data which consists of an object which has the property "ddel" as
45608  *      the drag proxy element. 
45609  */
45610     getDragData : function(e) {
45611         var target = this.findItemFromChild(e.getTarget());
45612                 if(target) {
45613                         this.handleSelection(e);
45614                         var selNodes = this.getSelectedNodes();
45615             var dragData = {
45616                 source: this,
45617                 copy: this.copy || (this.allowCopy && e.ctrlKey),
45618                 nodes: selNodes,
45619                 records: []
45620                         };
45621                         var selectedIndices = this.getSelectedIndexes();
45622                         for (var i = 0; i < selectedIndices.length; i++) {
45623                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
45624                         }
45625                         if (selNodes.length == 1) {
45626                                 dragData.ddel = target.cloneNode(true); // the div element
45627                         } else {
45628                                 var div = document.createElement('div'); // create the multi element drag "ghost"
45629                                 div.className = 'multi-proxy';
45630                                 for (var i = 0, len = selNodes.length; i < len; i++) {
45631                                         div.appendChild(selNodes[i].cloneNode(true));
45632                                 }
45633                                 dragData.ddel = div;
45634                         }
45635             //console.log(dragData)
45636             //console.log(dragData.ddel.innerHTML)
45637                         return dragData;
45638                 }
45639         //console.log('nodragData')
45640                 return false;
45641     },
45642     
45643 /**     Specify to which ddGroup items in this DDView may be dragged. */
45644     setDraggable: function(ddGroup) {
45645         if (ddGroup instanceof Array) {
45646                 Roo.each(ddGroup, this.setDraggable, this);
45647                 return;
45648         }
45649         if (this.dragZone) {
45650                 this.dragZone.addToGroup(ddGroup);
45651         } else {
45652                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
45653                                 containerScroll: true,
45654                                 ddGroup: ddGroup 
45655
45656                         });
45657 //                      Draggability implies selection. DragZone's mousedown selects the element.
45658                         if (!this.multiSelect) { this.singleSelect = true; }
45659
45660 //                      Wire the DragZone's handlers up to methods in *this*
45661                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
45662                 }
45663     },
45664
45665 /**     Specify from which ddGroup this DDView accepts drops. */
45666     setDroppable: function(ddGroup) {
45667         if (ddGroup instanceof Array) {
45668                 Roo.each(ddGroup, this.setDroppable, this);
45669                 return;
45670         }
45671         if (this.dropZone) {
45672                 this.dropZone.addToGroup(ddGroup);
45673         } else {
45674                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
45675                                 containerScroll: true,
45676                                 ddGroup: ddGroup
45677                         });
45678
45679 //                      Wire the DropZone's handlers up to methods in *this*
45680                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
45681                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
45682                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
45683                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
45684                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
45685                 }
45686     },
45687
45688 /**     Decide whether to drop above or below a View node. */
45689     getDropPoint : function(e, n, dd){
45690         if (n == this.el.dom) { return "above"; }
45691                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
45692                 var c = t + (b - t) / 2;
45693                 var y = Roo.lib.Event.getPageY(e);
45694                 if(y <= c) {
45695                         return "above";
45696                 }else{
45697                         return "below";
45698                 }
45699     },
45700
45701     onNodeEnter : function(n, dd, e, data){
45702                 return false;
45703     },
45704     
45705     onNodeOver : function(n, dd, e, data){
45706                 var pt = this.getDropPoint(e, n, dd);
45707                 // set the insert point style on the target node
45708                 var dragElClass = this.dropNotAllowed;
45709                 if (pt) {
45710                         var targetElClass;
45711                         if (pt == "above"){
45712                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
45713                                 targetElClass = "x-view-drag-insert-above";
45714                         } else {
45715                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
45716                                 targetElClass = "x-view-drag-insert-below";
45717                         }
45718                         if (this.lastInsertClass != targetElClass){
45719                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
45720                                 this.lastInsertClass = targetElClass;
45721                         }
45722                 }
45723                 return dragElClass;
45724         },
45725
45726     onNodeOut : function(n, dd, e, data){
45727                 this.removeDropIndicators(n);
45728     },
45729
45730     onNodeDrop : function(n, dd, e, data){
45731         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
45732                 return false;
45733         }
45734         var pt = this.getDropPoint(e, n, dd);
45735                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
45736                 if (pt == "below") { insertAt++; }
45737                 for (var i = 0; i < data.records.length; i++) {
45738                         var r = data.records[i];
45739                         var dup = this.store.getById(r.id);
45740                         if (dup && (dd != this.dragZone)) {
45741                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
45742                         } else {
45743                                 if (data.copy) {
45744                                         this.store.insert(insertAt++, r.copy());
45745                                 } else {
45746                                         data.source.isDirtyFlag = true;
45747                                         r.store.remove(r);
45748                                         this.store.insert(insertAt++, r);
45749                                 }
45750                                 this.isDirtyFlag = true;
45751                         }
45752                 }
45753                 this.dragZone.cachedTarget = null;
45754                 return true;
45755     },
45756
45757     removeDropIndicators : function(n){
45758                 if(n){
45759                         Roo.fly(n).removeClass([
45760                                 "x-view-drag-insert-above",
45761                                 "x-view-drag-insert-below"]);
45762                         this.lastInsertClass = "_noclass";
45763                 }
45764     },
45765
45766 /**
45767  *      Utility method. Add a delete option to the DDView's context menu.
45768  *      @param {String} imageUrl The URL of the "delete" icon image.
45769  */
45770         setDeletable: function(imageUrl) {
45771                 if (!this.singleSelect && !this.multiSelect) {
45772                         this.singleSelect = true;
45773                 }
45774                 var c = this.getContextMenu();
45775                 this.contextMenu.on("itemclick", function(item) {
45776                         switch (item.id) {
45777                                 case "delete":
45778                                         this.remove(this.getSelectedIndexes());
45779                                         break;
45780                         }
45781                 }, this);
45782                 this.contextMenu.add({
45783                         icon: imageUrl,
45784                         id: "delete",
45785                         text: 'Delete'
45786                 });
45787         },
45788         
45789 /**     Return the context menu for this DDView. */
45790         getContextMenu: function() {
45791                 if (!this.contextMenu) {
45792 //                      Create the View's context menu
45793                         this.contextMenu = new Roo.menu.Menu({
45794                                 id: this.id + "-contextmenu"
45795                         });
45796                         this.el.on("contextmenu", this.showContextMenu, this);
45797                 }
45798                 return this.contextMenu;
45799         },
45800         
45801         disableContextMenu: function() {
45802                 if (this.contextMenu) {
45803                         this.el.un("contextmenu", this.showContextMenu, this);
45804                 }
45805         },
45806
45807         showContextMenu: function(e, item) {
45808         item = this.findItemFromChild(e.getTarget());
45809                 if (item) {
45810                         e.stopEvent();
45811                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
45812                         this.contextMenu.showAt(e.getXY());
45813             }
45814     },
45815
45816 /**
45817  *      Remove {@link Roo.data.Record}s at the specified indices.
45818  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
45819  */
45820     remove: function(selectedIndices) {
45821                 selectedIndices = [].concat(selectedIndices);
45822                 for (var i = 0; i < selectedIndices.length; i++) {
45823                         var rec = this.store.getAt(selectedIndices[i]);
45824                         this.store.remove(rec);
45825                 }
45826     },
45827
45828 /**
45829  *      Double click fires the event, but also, if this is draggable, and there is only one other
45830  *      related DropZone, it transfers the selected node.
45831  */
45832     onDblClick : function(e){
45833         var item = this.findItemFromChild(e.getTarget());
45834         if(item){
45835             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
45836                 return false;
45837             }
45838             if (this.dragGroup) {
45839                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
45840                     while (targets.indexOf(this.dropZone) > -1) {
45841                             targets.remove(this.dropZone);
45842                                 }
45843                     if (targets.length == 1) {
45844                                         this.dragZone.cachedTarget = null;
45845                         var el = Roo.get(targets[0].getEl());
45846                         var box = el.getBox(true);
45847                         targets[0].onNodeDrop(el.dom, {
45848                                 target: el.dom,
45849                                 xy: [box.x, box.y + box.height - 1]
45850                         }, null, this.getDragData(e));
45851                     }
45852                 }
45853         }
45854     },
45855     
45856     handleSelection: function(e) {
45857                 this.dragZone.cachedTarget = null;
45858         var item = this.findItemFromChild(e.getTarget());
45859         if (!item) {
45860                 this.clearSelections(true);
45861                 return;
45862         }
45863                 if (item && (this.multiSelect || this.singleSelect)){
45864                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
45865                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
45866                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
45867                                 this.unselect(item);
45868                         } else {
45869                                 this.select(item, this.multiSelect && e.ctrlKey);
45870                                 this.lastSelection = item;
45871                         }
45872                 }
45873     },
45874
45875     onItemClick : function(item, index, e){
45876                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
45877                         return false;
45878                 }
45879                 return true;
45880     },
45881
45882     unselect : function(nodeInfo, suppressEvent){
45883                 var node = this.getNode(nodeInfo);
45884                 if(node && this.isSelected(node)){
45885                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
45886                                 Roo.fly(node).removeClass(this.selectedClass);
45887                                 this.selections.remove(node);
45888                                 if(!suppressEvent){
45889                                         this.fireEvent("selectionchange", this, this.selections);
45890                                 }
45891                         }
45892                 }
45893     }
45894 });
45895 /*
45896  * Based on:
45897  * Ext JS Library 1.1.1
45898  * Copyright(c) 2006-2007, Ext JS, LLC.
45899  *
45900  * Originally Released Under LGPL - original licence link has changed is not relivant.
45901  *
45902  * Fork - LGPL
45903  * <script type="text/javascript">
45904  */
45905  
45906 /**
45907  * @class Roo.LayoutManager
45908  * @extends Roo.util.Observable
45909  * Base class for layout managers.
45910  */
45911 Roo.LayoutManager = function(container, config){
45912     Roo.LayoutManager.superclass.constructor.call(this);
45913     this.el = Roo.get(container);
45914     // ie scrollbar fix
45915     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
45916         document.body.scroll = "no";
45917     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
45918         this.el.position('relative');
45919     }
45920     this.id = this.el.id;
45921     this.el.addClass("x-layout-container");
45922     /** false to disable window resize monitoring @type Boolean */
45923     this.monitorWindowResize = true;
45924     this.regions = {};
45925     this.addEvents({
45926         /**
45927          * @event layout
45928          * Fires when a layout is performed. 
45929          * @param {Roo.LayoutManager} this
45930          */
45931         "layout" : true,
45932         /**
45933          * @event regionresized
45934          * Fires when the user resizes a region. 
45935          * @param {Roo.LayoutRegion} region The resized region
45936          * @param {Number} newSize The new size (width for east/west, height for north/south)
45937          */
45938         "regionresized" : true,
45939         /**
45940          * @event regioncollapsed
45941          * Fires when a region is collapsed. 
45942          * @param {Roo.LayoutRegion} region The collapsed region
45943          */
45944         "regioncollapsed" : true,
45945         /**
45946          * @event regionexpanded
45947          * Fires when a region is expanded.  
45948          * @param {Roo.LayoutRegion} region The expanded region
45949          */
45950         "regionexpanded" : true
45951     });
45952     this.updating = false;
45953     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
45954 };
45955
45956 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
45957     /**
45958      * Returns true if this layout is currently being updated
45959      * @return {Boolean}
45960      */
45961     isUpdating : function(){
45962         return this.updating; 
45963     },
45964     
45965     /**
45966      * Suspend the LayoutManager from doing auto-layouts while
45967      * making multiple add or remove calls
45968      */
45969     beginUpdate : function(){
45970         this.updating = true;    
45971     },
45972     
45973     /**
45974      * Restore auto-layouts and optionally disable the manager from performing a layout
45975      * @param {Boolean} noLayout true to disable a layout update 
45976      */
45977     endUpdate : function(noLayout){
45978         this.updating = false;
45979         if(!noLayout){
45980             this.layout();
45981         }    
45982     },
45983     
45984     layout: function(){
45985         
45986     },
45987     
45988     onRegionResized : function(region, newSize){
45989         this.fireEvent("regionresized", region, newSize);
45990         this.layout();
45991     },
45992     
45993     onRegionCollapsed : function(region){
45994         this.fireEvent("regioncollapsed", region);
45995     },
45996     
45997     onRegionExpanded : function(region){
45998         this.fireEvent("regionexpanded", region);
45999     },
46000         
46001     /**
46002      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
46003      * performs box-model adjustments.
46004      * @return {Object} The size as an object {width: (the width), height: (the height)}
46005      */
46006     getViewSize : function(){
46007         var size;
46008         if(this.el.dom != document.body){
46009             size = this.el.getSize();
46010         }else{
46011             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
46012         }
46013         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
46014         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46015         return size;
46016     },
46017     
46018     /**
46019      * Returns the Element this layout is bound to.
46020      * @return {Roo.Element}
46021      */
46022     getEl : function(){
46023         return this.el;
46024     },
46025     
46026     /**
46027      * Returns the specified region.
46028      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
46029      * @return {Roo.LayoutRegion}
46030      */
46031     getRegion : function(target){
46032         return this.regions[target.toLowerCase()];
46033     },
46034     
46035     onWindowResize : function(){
46036         if(this.monitorWindowResize){
46037             this.layout();
46038         }
46039     }
46040 });/*
46041  * Based on:
46042  * Ext JS Library 1.1.1
46043  * Copyright(c) 2006-2007, Ext JS, LLC.
46044  *
46045  * Originally Released Under LGPL - original licence link has changed is not relivant.
46046  *
46047  * Fork - LGPL
46048  * <script type="text/javascript">
46049  */
46050 /**
46051  * @class Roo.BorderLayout
46052  * @extends Roo.LayoutManager
46053  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
46054  * please see: <br><br>
46055  * <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>
46056  * <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>
46057  * Example:
46058  <pre><code>
46059  var layout = new Roo.BorderLayout(document.body, {
46060     north: {
46061         initialSize: 25,
46062         titlebar: false
46063     },
46064     west: {
46065         split:true,
46066         initialSize: 200,
46067         minSize: 175,
46068         maxSize: 400,
46069         titlebar: true,
46070         collapsible: true
46071     },
46072     east: {
46073         split:true,
46074         initialSize: 202,
46075         minSize: 175,
46076         maxSize: 400,
46077         titlebar: true,
46078         collapsible: true
46079     },
46080     south: {
46081         split:true,
46082         initialSize: 100,
46083         minSize: 100,
46084         maxSize: 200,
46085         titlebar: true,
46086         collapsible: true
46087     },
46088     center: {
46089         titlebar: true,
46090         autoScroll:true,
46091         resizeTabs: true,
46092         minTabWidth: 50,
46093         preferredTabWidth: 150
46094     }
46095 });
46096
46097 // shorthand
46098 var CP = Roo.ContentPanel;
46099
46100 layout.beginUpdate();
46101 layout.add("north", new CP("north", "North"));
46102 layout.add("south", new CP("south", {title: "South", closable: true}));
46103 layout.add("west", new CP("west", {title: "West"}));
46104 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
46105 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
46106 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
46107 layout.getRegion("center").showPanel("center1");
46108 layout.endUpdate();
46109 </code></pre>
46110
46111 <b>The container the layout is rendered into can be either the body element or any other element.
46112 If it is not the body element, the container needs to either be an absolute positioned element,
46113 or you will need to add "position:relative" to the css of the container.  You will also need to specify
46114 the container size if it is not the body element.</b>
46115
46116 * @constructor
46117 * Create a new BorderLayout
46118 * @param {String/HTMLElement/Element} container The container this layout is bound to
46119 * @param {Object} config Configuration options
46120  */
46121 Roo.BorderLayout = function(container, config){
46122     config = config || {};
46123     Roo.BorderLayout.superclass.constructor.call(this, container, config);
46124     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
46125     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
46126         var target = this.factory.validRegions[i];
46127         if(config[target]){
46128             this.addRegion(target, config[target]);
46129         }
46130     }
46131 };
46132
46133 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
46134     /**
46135      * Creates and adds a new region if it doesn't already exist.
46136      * @param {String} target The target region key (north, south, east, west or center).
46137      * @param {Object} config The regions config object
46138      * @return {BorderLayoutRegion} The new region
46139      */
46140     addRegion : function(target, config){
46141         if(!this.regions[target]){
46142             var r = this.factory.create(target, this, config);
46143             this.bindRegion(target, r);
46144         }
46145         return this.regions[target];
46146     },
46147
46148     // private (kinda)
46149     bindRegion : function(name, r){
46150         this.regions[name] = r;
46151         r.on("visibilitychange", this.layout, this);
46152         r.on("paneladded", this.layout, this);
46153         r.on("panelremoved", this.layout, this);
46154         r.on("invalidated", this.layout, this);
46155         r.on("resized", this.onRegionResized, this);
46156         r.on("collapsed", this.onRegionCollapsed, this);
46157         r.on("expanded", this.onRegionExpanded, this);
46158     },
46159
46160     /**
46161      * Performs a layout update.
46162      */
46163     layout : function(){
46164         if(this.updating) return;
46165         var size = this.getViewSize();
46166         var w = size.width;
46167         var h = size.height;
46168         var centerW = w;
46169         var centerH = h;
46170         var centerY = 0;
46171         var centerX = 0;
46172         //var x = 0, y = 0;
46173
46174         var rs = this.regions;
46175         var north = rs["north"];
46176         var south = rs["south"]; 
46177         var west = rs["west"];
46178         var east = rs["east"];
46179         var center = rs["center"];
46180         //if(this.hideOnLayout){ // not supported anymore
46181             //c.el.setStyle("display", "none");
46182         //}
46183         if(north && north.isVisible()){
46184             var b = north.getBox();
46185             var m = north.getMargins();
46186             b.width = w - (m.left+m.right);
46187             b.x = m.left;
46188             b.y = m.top;
46189             centerY = b.height + b.y + m.bottom;
46190             centerH -= centerY;
46191             north.updateBox(this.safeBox(b));
46192         }
46193         if(south && south.isVisible()){
46194             var b = south.getBox();
46195             var m = south.getMargins();
46196             b.width = w - (m.left+m.right);
46197             b.x = m.left;
46198             var totalHeight = (b.height + m.top + m.bottom);
46199             b.y = h - totalHeight + m.top;
46200             centerH -= totalHeight;
46201             south.updateBox(this.safeBox(b));
46202         }
46203         if(west && west.isVisible()){
46204             var b = west.getBox();
46205             var m = west.getMargins();
46206             b.height = centerH - (m.top+m.bottom);
46207             b.x = m.left;
46208             b.y = centerY + m.top;
46209             var totalWidth = (b.width + m.left + m.right);
46210             centerX += totalWidth;
46211             centerW -= totalWidth;
46212             west.updateBox(this.safeBox(b));
46213         }
46214         if(east && east.isVisible()){
46215             var b = east.getBox();
46216             var m = east.getMargins();
46217             b.height = centerH - (m.top+m.bottom);
46218             var totalWidth = (b.width + m.left + m.right);
46219             b.x = w - totalWidth + m.left;
46220             b.y = centerY + m.top;
46221             centerW -= totalWidth;
46222             east.updateBox(this.safeBox(b));
46223         }
46224         if(center){
46225             var m = center.getMargins();
46226             var centerBox = {
46227                 x: centerX + m.left,
46228                 y: centerY + m.top,
46229                 width: centerW - (m.left+m.right),
46230                 height: centerH - (m.top+m.bottom)
46231             };
46232             //if(this.hideOnLayout){
46233                 //center.el.setStyle("display", "block");
46234             //}
46235             center.updateBox(this.safeBox(centerBox));
46236         }
46237         this.el.repaint();
46238         this.fireEvent("layout", this);
46239     },
46240
46241     // private
46242     safeBox : function(box){
46243         box.width = Math.max(0, box.width);
46244         box.height = Math.max(0, box.height);
46245         return box;
46246     },
46247
46248     /**
46249      * Adds a ContentPanel (or subclass) to this layout.
46250      * @param {String} target The target region key (north, south, east, west or center).
46251      * @param {Roo.ContentPanel} panel The panel to add
46252      * @return {Roo.ContentPanel} The added panel
46253      */
46254     add : function(target, panel){
46255          
46256         target = target.toLowerCase();
46257         return this.regions[target].add(panel);
46258     },
46259
46260     /**
46261      * Remove a ContentPanel (or subclass) to this layout.
46262      * @param {String} target The target region key (north, south, east, west or center).
46263      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
46264      * @return {Roo.ContentPanel} The removed panel
46265      */
46266     remove : function(target, panel){
46267         target = target.toLowerCase();
46268         return this.regions[target].remove(panel);
46269     },
46270
46271     /**
46272      * Searches all regions for a panel with the specified id
46273      * @param {String} panelId
46274      * @return {Roo.ContentPanel} The panel or null if it wasn't found
46275      */
46276     findPanel : function(panelId){
46277         var rs = this.regions;
46278         for(var target in rs){
46279             if(typeof rs[target] != "function"){
46280                 var p = rs[target].getPanel(panelId);
46281                 if(p){
46282                     return p;
46283                 }
46284             }
46285         }
46286         return null;
46287     },
46288
46289     /**
46290      * Searches all regions for a panel with the specified id and activates (shows) it.
46291      * @param {String/ContentPanel} panelId The panels id or the panel itself
46292      * @return {Roo.ContentPanel} The shown panel or null
46293      */
46294     showPanel : function(panelId) {
46295       var rs = this.regions;
46296       for(var target in rs){
46297          var r = rs[target];
46298          if(typeof r != "function"){
46299             if(r.hasPanel(panelId)){
46300                return r.showPanel(panelId);
46301             }
46302          }
46303       }
46304       return null;
46305    },
46306
46307    /**
46308      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
46309      * @param {Roo.state.Provider} provider (optional) An alternate state provider
46310      */
46311     restoreState : function(provider){
46312         if(!provider){
46313             provider = Roo.state.Manager;
46314         }
46315         var sm = new Roo.LayoutStateManager();
46316         sm.init(this, provider);
46317     },
46318
46319     /**
46320      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
46321      * object should contain properties for each region to add ContentPanels to, and each property's value should be
46322      * a valid ContentPanel config object.  Example:
46323      * <pre><code>
46324 // Create the main layout
46325 var layout = new Roo.BorderLayout('main-ct', {
46326     west: {
46327         split:true,
46328         minSize: 175,
46329         titlebar: true
46330     },
46331     center: {
46332         title:'Components'
46333     }
46334 }, 'main-ct');
46335
46336 // Create and add multiple ContentPanels at once via configs
46337 layout.batchAdd({
46338    west: {
46339        id: 'source-files',
46340        autoCreate:true,
46341        title:'Ext Source Files',
46342        autoScroll:true,
46343        fitToFrame:true
46344    },
46345    center : {
46346        el: cview,
46347        autoScroll:true,
46348        fitToFrame:true,
46349        toolbar: tb,
46350        resizeEl:'cbody'
46351    }
46352 });
46353 </code></pre>
46354      * @param {Object} regions An object containing ContentPanel configs by region name
46355      */
46356     batchAdd : function(regions){
46357         this.beginUpdate();
46358         for(var rname in regions){
46359             var lr = this.regions[rname];
46360             if(lr){
46361                 this.addTypedPanels(lr, regions[rname]);
46362             }
46363         }
46364         this.endUpdate();
46365     },
46366
46367     // private
46368     addTypedPanels : function(lr, ps){
46369         if(typeof ps == 'string'){
46370             lr.add(new Roo.ContentPanel(ps));
46371         }
46372         else if(ps instanceof Array){
46373             for(var i =0, len = ps.length; i < len; i++){
46374                 this.addTypedPanels(lr, ps[i]);
46375             }
46376         }
46377         else if(!ps.events){ // raw config?
46378             var el = ps.el;
46379             delete ps.el; // prevent conflict
46380             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
46381         }
46382         else {  // panel object assumed!
46383             lr.add(ps);
46384         }
46385     },
46386     /**
46387      * Adds a xtype elements to the layout.
46388      * <pre><code>
46389
46390 layout.addxtype({
46391        xtype : 'ContentPanel',
46392        region: 'west',
46393        items: [ .... ]
46394    }
46395 );
46396
46397 layout.addxtype({
46398         xtype : 'NestedLayoutPanel',
46399         region: 'west',
46400         layout: {
46401            center: { },
46402            west: { }   
46403         },
46404         items : [ ... list of content panels or nested layout panels.. ]
46405    }
46406 );
46407 </code></pre>
46408      * @param {Object} cfg Xtype definition of item to add.
46409      */
46410     addxtype : function(cfg)
46411     {
46412         // basically accepts a pannel...
46413         // can accept a layout region..!?!?
46414         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
46415         
46416         if (!cfg.xtype.match(/Panel$/)) {
46417             return false;
46418         }
46419         var ret = false;
46420         
46421         if (typeof(cfg.region) == 'undefined') {
46422             Roo.log("Failed to add Panel, region was not set");
46423             Roo.log(cfg);
46424             return false;
46425         }
46426         var region = cfg.region;
46427         delete cfg.region;
46428         
46429           
46430         var xitems = [];
46431         if (cfg.items) {
46432             xitems = cfg.items;
46433             delete cfg.items;
46434         }
46435         var nb = false;
46436         
46437         switch(cfg.xtype) 
46438         {
46439             case 'ContentPanel':  // ContentPanel (el, cfg)
46440             case 'ScrollPanel':  // ContentPanel (el, cfg)
46441                 if(cfg.autoCreate) {
46442                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
46443                 } else {
46444                     var el = this.el.createChild();
46445                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
46446                 }
46447                 
46448                 this.add(region, ret);
46449                 break;
46450             
46451             
46452             case 'TreePanel': // our new panel!
46453                 cfg.el = this.el.createChild();
46454                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
46455                 this.add(region, ret);
46456                 break;
46457             
46458             case 'NestedLayoutPanel': 
46459                 // create a new Layout (which is  a Border Layout...
46460                 var el = this.el.createChild();
46461                 var clayout = cfg.layout;
46462                 delete cfg.layout;
46463                 clayout.items   = clayout.items  || [];
46464                 // replace this exitems with the clayout ones..
46465                 xitems = clayout.items;
46466                  
46467                 
46468                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
46469                     cfg.background = false;
46470                 }
46471                 var layout = new Roo.BorderLayout(el, clayout);
46472                 
46473                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
46474                 //console.log('adding nested layout panel '  + cfg.toSource());
46475                 this.add(region, ret);
46476                 nb = {}; /// find first...
46477                 break;
46478                 
46479             case 'GridPanel': 
46480             
46481                 // needs grid and region
46482                 
46483                 //var el = this.getRegion(region).el.createChild();
46484                 var el = this.el.createChild();
46485                 // create the grid first...
46486                 
46487                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
46488                 delete cfg.grid;
46489                 if (region == 'center' && this.active ) {
46490                     cfg.background = false;
46491                 }
46492                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
46493                 
46494                 this.add(region, ret);
46495                 if (cfg.background) {
46496                     ret.on('activate', function(gp) {
46497                         if (!gp.grid.rendered) {
46498                             gp.grid.render();
46499                         }
46500                     });
46501                 } else {
46502                     grid.render();
46503                 }
46504                 break;
46505            
46506                
46507                 
46508                 
46509             default: 
46510                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
46511                 return null;
46512              // GridPanel (grid, cfg)
46513             
46514         }
46515         this.beginUpdate();
46516         // add children..
46517         var region = '';
46518         var abn = {};
46519         Roo.each(xitems, function(i)  {
46520             region = nb && i.region ? i.region : false;
46521             
46522             var add = ret.addxtype(i);
46523            
46524             if (region) {
46525                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
46526                 if (!i.background) {
46527                     abn[region] = nb[region] ;
46528                 }
46529             }
46530             
46531         });
46532         this.endUpdate();
46533
46534         // make the last non-background panel active..
46535         //if (nb) { Roo.log(abn); }
46536         if (nb) {
46537             
46538             for(var r in abn) {
46539                 region = this.getRegion(r);
46540                 if (region) {
46541                     // tried using nb[r], but it does not work..
46542                      
46543                     region.showPanel(abn[r]);
46544                    
46545                 }
46546             }
46547         }
46548         return ret;
46549         
46550     }
46551 });
46552
46553 /**
46554  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
46555  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
46556  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
46557  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
46558  * <pre><code>
46559 // shorthand
46560 var CP = Roo.ContentPanel;
46561
46562 var layout = Roo.BorderLayout.create({
46563     north: {
46564         initialSize: 25,
46565         titlebar: false,
46566         panels: [new CP("north", "North")]
46567     },
46568     west: {
46569         split:true,
46570         initialSize: 200,
46571         minSize: 175,
46572         maxSize: 400,
46573         titlebar: true,
46574         collapsible: true,
46575         panels: [new CP("west", {title: "West"})]
46576     },
46577     east: {
46578         split:true,
46579         initialSize: 202,
46580         minSize: 175,
46581         maxSize: 400,
46582         titlebar: true,
46583         collapsible: true,
46584         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
46585     },
46586     south: {
46587         split:true,
46588         initialSize: 100,
46589         minSize: 100,
46590         maxSize: 200,
46591         titlebar: true,
46592         collapsible: true,
46593         panels: [new CP("south", {title: "South", closable: true})]
46594     },
46595     center: {
46596         titlebar: true,
46597         autoScroll:true,
46598         resizeTabs: true,
46599         minTabWidth: 50,
46600         preferredTabWidth: 150,
46601         panels: [
46602             new CP("center1", {title: "Close Me", closable: true}),
46603             new CP("center2", {title: "Center Panel", closable: false})
46604         ]
46605     }
46606 }, document.body);
46607
46608 layout.getRegion("center").showPanel("center1");
46609 </code></pre>
46610  * @param config
46611  * @param targetEl
46612  */
46613 Roo.BorderLayout.create = function(config, targetEl){
46614     var layout = new Roo.BorderLayout(targetEl || document.body, config);
46615     layout.beginUpdate();
46616     var regions = Roo.BorderLayout.RegionFactory.validRegions;
46617     for(var j = 0, jlen = regions.length; j < jlen; j++){
46618         var lr = regions[j];
46619         if(layout.regions[lr] && config[lr].panels){
46620             var r = layout.regions[lr];
46621             var ps = config[lr].panels;
46622             layout.addTypedPanels(r, ps);
46623         }
46624     }
46625     layout.endUpdate();
46626     return layout;
46627 };
46628
46629 // private
46630 Roo.BorderLayout.RegionFactory = {
46631     // private
46632     validRegions : ["north","south","east","west","center"],
46633
46634     // private
46635     create : function(target, mgr, config){
46636         target = target.toLowerCase();
46637         if(config.lightweight || config.basic){
46638             return new Roo.BasicLayoutRegion(mgr, config, target);
46639         }
46640         switch(target){
46641             case "north":
46642                 return new Roo.NorthLayoutRegion(mgr, config);
46643             case "south":
46644                 return new Roo.SouthLayoutRegion(mgr, config);
46645             case "east":
46646                 return new Roo.EastLayoutRegion(mgr, config);
46647             case "west":
46648                 return new Roo.WestLayoutRegion(mgr, config);
46649             case "center":
46650                 return new Roo.CenterLayoutRegion(mgr, config);
46651         }
46652         throw 'Layout region "'+target+'" not supported.';
46653     }
46654 };/*
46655  * Based on:
46656  * Ext JS Library 1.1.1
46657  * Copyright(c) 2006-2007, Ext JS, LLC.
46658  *
46659  * Originally Released Under LGPL - original licence link has changed is not relivant.
46660  *
46661  * Fork - LGPL
46662  * <script type="text/javascript">
46663  */
46664  
46665 /**
46666  * @class Roo.BasicLayoutRegion
46667  * @extends Roo.util.Observable
46668  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
46669  * and does not have a titlebar, tabs or any other features. All it does is size and position 
46670  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
46671  */
46672 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
46673     this.mgr = mgr;
46674     this.position  = pos;
46675     this.events = {
46676         /**
46677          * @scope Roo.BasicLayoutRegion
46678          */
46679         
46680         /**
46681          * @event beforeremove
46682          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
46683          * @param {Roo.LayoutRegion} this
46684          * @param {Roo.ContentPanel} panel The panel
46685          * @param {Object} e The cancel event object
46686          */
46687         "beforeremove" : true,
46688         /**
46689          * @event invalidated
46690          * Fires when the layout for this region is changed.
46691          * @param {Roo.LayoutRegion} this
46692          */
46693         "invalidated" : true,
46694         /**
46695          * @event visibilitychange
46696          * Fires when this region is shown or hidden 
46697          * @param {Roo.LayoutRegion} this
46698          * @param {Boolean} visibility true or false
46699          */
46700         "visibilitychange" : true,
46701         /**
46702          * @event paneladded
46703          * Fires when a panel is added. 
46704          * @param {Roo.LayoutRegion} this
46705          * @param {Roo.ContentPanel} panel The panel
46706          */
46707         "paneladded" : true,
46708         /**
46709          * @event panelremoved
46710          * Fires when a panel is removed. 
46711          * @param {Roo.LayoutRegion} this
46712          * @param {Roo.ContentPanel} panel The panel
46713          */
46714         "panelremoved" : true,
46715         /**
46716          * @event collapsed
46717          * Fires when this region is collapsed.
46718          * @param {Roo.LayoutRegion} this
46719          */
46720         "collapsed" : true,
46721         /**
46722          * @event expanded
46723          * Fires when this region is expanded.
46724          * @param {Roo.LayoutRegion} this
46725          */
46726         "expanded" : true,
46727         /**
46728          * @event slideshow
46729          * Fires when this region is slid into view.
46730          * @param {Roo.LayoutRegion} this
46731          */
46732         "slideshow" : true,
46733         /**
46734          * @event slidehide
46735          * Fires when this region slides out of view. 
46736          * @param {Roo.LayoutRegion} this
46737          */
46738         "slidehide" : true,
46739         /**
46740          * @event panelactivated
46741          * Fires when a panel is activated. 
46742          * @param {Roo.LayoutRegion} this
46743          * @param {Roo.ContentPanel} panel The activated panel
46744          */
46745         "panelactivated" : true,
46746         /**
46747          * @event resized
46748          * Fires when the user resizes this region. 
46749          * @param {Roo.LayoutRegion} this
46750          * @param {Number} newSize The new size (width for east/west, height for north/south)
46751          */
46752         "resized" : true
46753     };
46754     /** A collection of panels in this region. @type Roo.util.MixedCollection */
46755     this.panels = new Roo.util.MixedCollection();
46756     this.panels.getKey = this.getPanelId.createDelegate(this);
46757     this.box = null;
46758     this.activePanel = null;
46759     // ensure listeners are added...
46760     
46761     if (config.listeners || config.events) {
46762         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
46763             listeners : config.listeners || {},
46764             events : config.events || {}
46765         });
46766     }
46767     
46768     if(skipConfig !== true){
46769         this.applyConfig(config);
46770     }
46771 };
46772
46773 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
46774     getPanelId : function(p){
46775         return p.getId();
46776     },
46777     
46778     applyConfig : function(config){
46779         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
46780         this.config = config;
46781         
46782     },
46783     
46784     /**
46785      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
46786      * the width, for horizontal (north, south) the height.
46787      * @param {Number} newSize The new width or height
46788      */
46789     resizeTo : function(newSize){
46790         var el = this.el ? this.el :
46791                  (this.activePanel ? this.activePanel.getEl() : null);
46792         if(el){
46793             switch(this.position){
46794                 case "east":
46795                 case "west":
46796                     el.setWidth(newSize);
46797                     this.fireEvent("resized", this, newSize);
46798                 break;
46799                 case "north":
46800                 case "south":
46801                     el.setHeight(newSize);
46802                     this.fireEvent("resized", this, newSize);
46803                 break;                
46804             }
46805         }
46806     },
46807     
46808     getBox : function(){
46809         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
46810     },
46811     
46812     getMargins : function(){
46813         return this.margins;
46814     },
46815     
46816     updateBox : function(box){
46817         this.box = box;
46818         var el = this.activePanel.getEl();
46819         el.dom.style.left = box.x + "px";
46820         el.dom.style.top = box.y + "px";
46821         this.activePanel.setSize(box.width, box.height);
46822     },
46823     
46824     /**
46825      * Returns the container element for this region.
46826      * @return {Roo.Element}
46827      */
46828     getEl : function(){
46829         return this.activePanel;
46830     },
46831     
46832     /**
46833      * Returns true if this region is currently visible.
46834      * @return {Boolean}
46835      */
46836     isVisible : function(){
46837         return this.activePanel ? true : false;
46838     },
46839     
46840     setActivePanel : function(panel){
46841         panel = this.getPanel(panel);
46842         if(this.activePanel && this.activePanel != panel){
46843             this.activePanel.setActiveState(false);
46844             this.activePanel.getEl().setLeftTop(-10000,-10000);
46845         }
46846         this.activePanel = panel;
46847         panel.setActiveState(true);
46848         if(this.box){
46849             panel.setSize(this.box.width, this.box.height);
46850         }
46851         this.fireEvent("panelactivated", this, panel);
46852         this.fireEvent("invalidated");
46853     },
46854     
46855     /**
46856      * Show the specified panel.
46857      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
46858      * @return {Roo.ContentPanel} The shown panel or null
46859      */
46860     showPanel : function(panel){
46861         if(panel = this.getPanel(panel)){
46862             this.setActivePanel(panel);
46863         }
46864         return panel;
46865     },
46866     
46867     /**
46868      * Get the active panel for this region.
46869      * @return {Roo.ContentPanel} The active panel or null
46870      */
46871     getActivePanel : function(){
46872         return this.activePanel;
46873     },
46874     
46875     /**
46876      * Add the passed ContentPanel(s)
46877      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
46878      * @return {Roo.ContentPanel} The panel added (if only one was added)
46879      */
46880     add : function(panel){
46881         if(arguments.length > 1){
46882             for(var i = 0, len = arguments.length; i < len; i++) {
46883                 this.add(arguments[i]);
46884             }
46885             return null;
46886         }
46887         if(this.hasPanel(panel)){
46888             this.showPanel(panel);
46889             return panel;
46890         }
46891         var el = panel.getEl();
46892         if(el.dom.parentNode != this.mgr.el.dom){
46893             this.mgr.el.dom.appendChild(el.dom);
46894         }
46895         if(panel.setRegion){
46896             panel.setRegion(this);
46897         }
46898         this.panels.add(panel);
46899         el.setStyle("position", "absolute");
46900         if(!panel.background){
46901             this.setActivePanel(panel);
46902             if(this.config.initialSize && this.panels.getCount()==1){
46903                 this.resizeTo(this.config.initialSize);
46904             }
46905         }
46906         this.fireEvent("paneladded", this, panel);
46907         return panel;
46908     },
46909     
46910     /**
46911      * Returns true if the panel is in this region.
46912      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
46913      * @return {Boolean}
46914      */
46915     hasPanel : function(panel){
46916         if(typeof panel == "object"){ // must be panel obj
46917             panel = panel.getId();
46918         }
46919         return this.getPanel(panel) ? true : false;
46920     },
46921     
46922     /**
46923      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
46924      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
46925      * @param {Boolean} preservePanel Overrides the config preservePanel option
46926      * @return {Roo.ContentPanel} The panel that was removed
46927      */
46928     remove : function(panel, preservePanel){
46929         panel = this.getPanel(panel);
46930         if(!panel){
46931             return null;
46932         }
46933         var e = {};
46934         this.fireEvent("beforeremove", this, panel, e);
46935         if(e.cancel === true){
46936             return null;
46937         }
46938         var panelId = panel.getId();
46939         this.panels.removeKey(panelId);
46940         return panel;
46941     },
46942     
46943     /**
46944      * Returns the panel specified or null if it's not in this region.
46945      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
46946      * @return {Roo.ContentPanel}
46947      */
46948     getPanel : function(id){
46949         if(typeof id == "object"){ // must be panel obj
46950             return id;
46951         }
46952         return this.panels.get(id);
46953     },
46954     
46955     /**
46956      * Returns this regions position (north/south/east/west/center).
46957      * @return {String} 
46958      */
46959     getPosition: function(){
46960         return this.position;    
46961     }
46962 });/*
46963  * Based on:
46964  * Ext JS Library 1.1.1
46965  * Copyright(c) 2006-2007, Ext JS, LLC.
46966  *
46967  * Originally Released Under LGPL - original licence link has changed is not relivant.
46968  *
46969  * Fork - LGPL
46970  * <script type="text/javascript">
46971  */
46972  
46973 /**
46974  * @class Roo.LayoutRegion
46975  * @extends Roo.BasicLayoutRegion
46976  * This class represents a region in a layout manager.
46977  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
46978  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
46979  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
46980  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
46981  * @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})
46982  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
46983  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
46984  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
46985  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
46986  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
46987  * @cfg {String}    title           The title for the region (overrides panel titles)
46988  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
46989  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
46990  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
46991  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
46992  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
46993  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
46994  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
46995  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
46996  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
46997  * @cfg {Boolean}   showPin         True to show a pin button
46998  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
46999  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
47000  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
47001  * @cfg {Number}    width           For East/West panels
47002  * @cfg {Number}    height          For North/South panels
47003  * @cfg {Boolean}   split           To show the splitter
47004  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
47005  */
47006 Roo.LayoutRegion = function(mgr, config, pos){
47007     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
47008     var dh = Roo.DomHelper;
47009     /** This region's container element 
47010     * @type Roo.Element */
47011     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
47012     /** This region's title element 
47013     * @type Roo.Element */
47014
47015     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
47016         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
47017         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
47018     ]}, true);
47019     this.titleEl.enableDisplayMode();
47020     /** This region's title text element 
47021     * @type HTMLElement */
47022     this.titleTextEl = this.titleEl.dom.firstChild;
47023     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
47024     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
47025     this.closeBtn.enableDisplayMode();
47026     this.closeBtn.on("click", this.closeClicked, this);
47027     this.closeBtn.hide();
47028
47029     this.createBody(config);
47030     this.visible = true;
47031     this.collapsed = false;
47032
47033     if(config.hideWhenEmpty){
47034         this.hide();
47035         this.on("paneladded", this.validateVisibility, this);
47036         this.on("panelremoved", this.validateVisibility, this);
47037     }
47038     this.applyConfig(config);
47039 };
47040
47041 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
47042
47043     createBody : function(){
47044         /** This region's body element 
47045         * @type Roo.Element */
47046         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
47047     },
47048
47049     applyConfig : function(c){
47050         if(c.collapsible && this.position != "center" && !this.collapsedEl){
47051             var dh = Roo.DomHelper;
47052             if(c.titlebar !== false){
47053                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
47054                 this.collapseBtn.on("click", this.collapse, this);
47055                 this.collapseBtn.enableDisplayMode();
47056
47057                 if(c.showPin === true || this.showPin){
47058                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
47059                     this.stickBtn.enableDisplayMode();
47060                     this.stickBtn.on("click", this.expand, this);
47061                     this.stickBtn.hide();
47062                 }
47063             }
47064             /** This region's collapsed element
47065             * @type Roo.Element */
47066             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
47067                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
47068             ]}, true);
47069             if(c.floatable !== false){
47070                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
47071                this.collapsedEl.on("click", this.collapseClick, this);
47072             }
47073
47074             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
47075                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
47076                    id: "message", unselectable: "on", style:{"float":"left"}});
47077                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
47078              }
47079             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
47080             this.expandBtn.on("click", this.expand, this);
47081         }
47082         if(this.collapseBtn){
47083             this.collapseBtn.setVisible(c.collapsible == true);
47084         }
47085         this.cmargins = c.cmargins || this.cmargins ||
47086                          (this.position == "west" || this.position == "east" ?
47087                              {top: 0, left: 2, right:2, bottom: 0} :
47088                              {top: 2, left: 0, right:0, bottom: 2});
47089         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
47090         this.bottomTabs = c.tabPosition != "top";
47091         this.autoScroll = c.autoScroll || false;
47092         if(this.autoScroll){
47093             this.bodyEl.setStyle("overflow", "auto");
47094         }else{
47095             this.bodyEl.setStyle("overflow", "hidden");
47096         }
47097         //if(c.titlebar !== false){
47098             if((!c.titlebar && !c.title) || c.titlebar === false){
47099                 this.titleEl.hide();
47100             }else{
47101                 this.titleEl.show();
47102                 if(c.title){
47103                     this.titleTextEl.innerHTML = c.title;
47104                 }
47105             }
47106         //}
47107         this.duration = c.duration || .30;
47108         this.slideDuration = c.slideDuration || .45;
47109         this.config = c;
47110         if(c.collapsed){
47111             this.collapse(true);
47112         }
47113         if(c.hidden){
47114             this.hide();
47115         }
47116     },
47117     /**
47118      * Returns true if this region is currently visible.
47119      * @return {Boolean}
47120      */
47121     isVisible : function(){
47122         return this.visible;
47123     },
47124
47125     /**
47126      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
47127      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
47128      */
47129     setCollapsedTitle : function(title){
47130         title = title || "&#160;";
47131         if(this.collapsedTitleTextEl){
47132             this.collapsedTitleTextEl.innerHTML = title;
47133         }
47134     },
47135
47136     getBox : function(){
47137         var b;
47138         if(!this.collapsed){
47139             b = this.el.getBox(false, true);
47140         }else{
47141             b = this.collapsedEl.getBox(false, true);
47142         }
47143         return b;
47144     },
47145
47146     getMargins : function(){
47147         return this.collapsed ? this.cmargins : this.margins;
47148     },
47149
47150     highlight : function(){
47151         this.el.addClass("x-layout-panel-dragover");
47152     },
47153
47154     unhighlight : function(){
47155         this.el.removeClass("x-layout-panel-dragover");
47156     },
47157
47158     updateBox : function(box){
47159         this.box = box;
47160         if(!this.collapsed){
47161             this.el.dom.style.left = box.x + "px";
47162             this.el.dom.style.top = box.y + "px";
47163             this.updateBody(box.width, box.height);
47164         }else{
47165             this.collapsedEl.dom.style.left = box.x + "px";
47166             this.collapsedEl.dom.style.top = box.y + "px";
47167             this.collapsedEl.setSize(box.width, box.height);
47168         }
47169         if(this.tabs){
47170             this.tabs.autoSizeTabs();
47171         }
47172     },
47173
47174     updateBody : function(w, h){
47175         if(w !== null){
47176             this.el.setWidth(w);
47177             w -= this.el.getBorderWidth("rl");
47178             if(this.config.adjustments){
47179                 w += this.config.adjustments[0];
47180             }
47181         }
47182         if(h !== null){
47183             this.el.setHeight(h);
47184             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
47185             h -= this.el.getBorderWidth("tb");
47186             if(this.config.adjustments){
47187                 h += this.config.adjustments[1];
47188             }
47189             this.bodyEl.setHeight(h);
47190             if(this.tabs){
47191                 h = this.tabs.syncHeight(h);
47192             }
47193         }
47194         if(this.panelSize){
47195             w = w !== null ? w : this.panelSize.width;
47196             h = h !== null ? h : this.panelSize.height;
47197         }
47198         if(this.activePanel){
47199             var el = this.activePanel.getEl();
47200             w = w !== null ? w : el.getWidth();
47201             h = h !== null ? h : el.getHeight();
47202             this.panelSize = {width: w, height: h};
47203             this.activePanel.setSize(w, h);
47204         }
47205         if(Roo.isIE && this.tabs){
47206             this.tabs.el.repaint();
47207         }
47208     },
47209
47210     /**
47211      * Returns the container element for this region.
47212      * @return {Roo.Element}
47213      */
47214     getEl : function(){
47215         return this.el;
47216     },
47217
47218     /**
47219      * Hides this region.
47220      */
47221     hide : function(){
47222         if(!this.collapsed){
47223             this.el.dom.style.left = "-2000px";
47224             this.el.hide();
47225         }else{
47226             this.collapsedEl.dom.style.left = "-2000px";
47227             this.collapsedEl.hide();
47228         }
47229         this.visible = false;
47230         this.fireEvent("visibilitychange", this, false);
47231     },
47232
47233     /**
47234      * Shows this region if it was previously hidden.
47235      */
47236     show : function(){
47237         if(!this.collapsed){
47238             this.el.show();
47239         }else{
47240             this.collapsedEl.show();
47241         }
47242         this.visible = true;
47243         this.fireEvent("visibilitychange", this, true);
47244     },
47245
47246     closeClicked : function(){
47247         if(this.activePanel){
47248             this.remove(this.activePanel);
47249         }
47250     },
47251
47252     collapseClick : function(e){
47253         if(this.isSlid){
47254            e.stopPropagation();
47255            this.slideIn();
47256         }else{
47257            e.stopPropagation();
47258            this.slideOut();
47259         }
47260     },
47261
47262     /**
47263      * Collapses this region.
47264      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
47265      */
47266     collapse : function(skipAnim){
47267         if(this.collapsed) return;
47268         this.collapsed = true;
47269         if(this.split){
47270             this.split.el.hide();
47271         }
47272         if(this.config.animate && skipAnim !== true){
47273             this.fireEvent("invalidated", this);
47274             this.animateCollapse();
47275         }else{
47276             this.el.setLocation(-20000,-20000);
47277             this.el.hide();
47278             this.collapsedEl.show();
47279             this.fireEvent("collapsed", this);
47280             this.fireEvent("invalidated", this);
47281         }
47282     },
47283
47284     animateCollapse : function(){
47285         // overridden
47286     },
47287
47288     /**
47289      * Expands this region if it was previously collapsed.
47290      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
47291      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
47292      */
47293     expand : function(e, skipAnim){
47294         if(e) e.stopPropagation();
47295         if(!this.collapsed || this.el.hasActiveFx()) return;
47296         if(this.isSlid){
47297             this.afterSlideIn();
47298             skipAnim = true;
47299         }
47300         this.collapsed = false;
47301         if(this.config.animate && skipAnim !== true){
47302             this.animateExpand();
47303         }else{
47304             this.el.show();
47305             if(this.split){
47306                 this.split.el.show();
47307             }
47308             this.collapsedEl.setLocation(-2000,-2000);
47309             this.collapsedEl.hide();
47310             this.fireEvent("invalidated", this);
47311             this.fireEvent("expanded", this);
47312         }
47313     },
47314
47315     animateExpand : function(){
47316         // overridden
47317     },
47318
47319     initTabs : function()
47320     {
47321         this.bodyEl.setStyle("overflow", "hidden");
47322         var ts = new Roo.TabPanel(
47323                 this.bodyEl.dom,
47324                 {
47325                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
47326                     disableTooltips: this.config.disableTabTips,
47327                     toolbar : this.config.toolbar
47328                 }
47329         );
47330         if(this.config.hideTabs){
47331             ts.stripWrap.setDisplayed(false);
47332         }
47333         this.tabs = ts;
47334         ts.resizeTabs = this.config.resizeTabs === true;
47335         ts.minTabWidth = this.config.minTabWidth || 40;
47336         ts.maxTabWidth = this.config.maxTabWidth || 250;
47337         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
47338         ts.monitorResize = false;
47339         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
47340         ts.bodyEl.addClass('x-layout-tabs-body');
47341         this.panels.each(this.initPanelAsTab, this);
47342     },
47343
47344     initPanelAsTab : function(panel){
47345         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
47346                     this.config.closeOnTab && panel.isClosable());
47347         if(panel.tabTip !== undefined){
47348             ti.setTooltip(panel.tabTip);
47349         }
47350         ti.on("activate", function(){
47351               this.setActivePanel(panel);
47352         }, this);
47353         if(this.config.closeOnTab){
47354             ti.on("beforeclose", function(t, e){
47355                 e.cancel = true;
47356                 this.remove(panel);
47357             }, this);
47358         }
47359         return ti;
47360     },
47361
47362     updatePanelTitle : function(panel, title){
47363         if(this.activePanel == panel){
47364             this.updateTitle(title);
47365         }
47366         if(this.tabs){
47367             var ti = this.tabs.getTab(panel.getEl().id);
47368             ti.setText(title);
47369             if(panel.tabTip !== undefined){
47370                 ti.setTooltip(panel.tabTip);
47371             }
47372         }
47373     },
47374
47375     updateTitle : function(title){
47376         if(this.titleTextEl && !this.config.title){
47377             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
47378         }
47379     },
47380
47381     setActivePanel : function(panel){
47382         panel = this.getPanel(panel);
47383         if(this.activePanel && this.activePanel != panel){
47384             this.activePanel.setActiveState(false);
47385         }
47386         this.activePanel = panel;
47387         panel.setActiveState(true);
47388         if(this.panelSize){
47389             panel.setSize(this.panelSize.width, this.panelSize.height);
47390         }
47391         if(this.closeBtn){
47392             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
47393         }
47394         this.updateTitle(panel.getTitle());
47395         if(this.tabs){
47396             this.fireEvent("invalidated", this);
47397         }
47398         this.fireEvent("panelactivated", this, panel);
47399     },
47400
47401     /**
47402      * Shows the specified panel.
47403      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
47404      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
47405      */
47406     showPanel : function(panel){
47407         if(panel = this.getPanel(panel)){
47408             if(this.tabs){
47409                 var tab = this.tabs.getTab(panel.getEl().id);
47410                 if(tab.isHidden()){
47411                     this.tabs.unhideTab(tab.id);
47412                 }
47413                 tab.activate();
47414             }else{
47415                 this.setActivePanel(panel);
47416             }
47417         }
47418         return panel;
47419     },
47420
47421     /**
47422      * Get the active panel for this region.
47423      * @return {Roo.ContentPanel} The active panel or null
47424      */
47425     getActivePanel : function(){
47426         return this.activePanel;
47427     },
47428
47429     validateVisibility : function(){
47430         if(this.panels.getCount() < 1){
47431             this.updateTitle("&#160;");
47432             this.closeBtn.hide();
47433             this.hide();
47434         }else{
47435             if(!this.isVisible()){
47436                 this.show();
47437             }
47438         }
47439     },
47440
47441     /**
47442      * Adds the passed ContentPanel(s) to this region.
47443      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
47444      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
47445      */
47446     add : function(panel){
47447         if(arguments.length > 1){
47448             for(var i = 0, len = arguments.length; i < len; i++) {
47449                 this.add(arguments[i]);
47450             }
47451             return null;
47452         }
47453         if(this.hasPanel(panel)){
47454             this.showPanel(panel);
47455             return panel;
47456         }
47457         panel.setRegion(this);
47458         this.panels.add(panel);
47459         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
47460             this.bodyEl.dom.appendChild(panel.getEl().dom);
47461             if(panel.background !== true){
47462                 this.setActivePanel(panel);
47463             }
47464             this.fireEvent("paneladded", this, panel);
47465             return panel;
47466         }
47467         if(!this.tabs){
47468             this.initTabs();
47469         }else{
47470             this.initPanelAsTab(panel);
47471         }
47472         if(panel.background !== true){
47473             this.tabs.activate(panel.getEl().id);
47474         }
47475         this.fireEvent("paneladded", this, panel);
47476         return panel;
47477     },
47478
47479     /**
47480      * Hides the tab for the specified panel.
47481      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
47482      */
47483     hidePanel : function(panel){
47484         if(this.tabs && (panel = this.getPanel(panel))){
47485             this.tabs.hideTab(panel.getEl().id);
47486         }
47487     },
47488
47489     /**
47490      * Unhides the tab for a previously hidden panel.
47491      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
47492      */
47493     unhidePanel : function(panel){
47494         if(this.tabs && (panel = this.getPanel(panel))){
47495             this.tabs.unhideTab(panel.getEl().id);
47496         }
47497     },
47498
47499     clearPanels : function(){
47500         while(this.panels.getCount() > 0){
47501              this.remove(this.panels.first());
47502         }
47503     },
47504
47505     /**
47506      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
47507      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
47508      * @param {Boolean} preservePanel Overrides the config preservePanel option
47509      * @return {Roo.ContentPanel} The panel that was removed
47510      */
47511     remove : function(panel, preservePanel){
47512         panel = this.getPanel(panel);
47513         if(!panel){
47514             return null;
47515         }
47516         var e = {};
47517         this.fireEvent("beforeremove", this, panel, e);
47518         if(e.cancel === true){
47519             return null;
47520         }
47521         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
47522         var panelId = panel.getId();
47523         this.panels.removeKey(panelId);
47524         if(preservePanel){
47525             document.body.appendChild(panel.getEl().dom);
47526         }
47527         if(this.tabs){
47528             this.tabs.removeTab(panel.getEl().id);
47529         }else if (!preservePanel){
47530             this.bodyEl.dom.removeChild(panel.getEl().dom);
47531         }
47532         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
47533             var p = this.panels.first();
47534             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
47535             tempEl.appendChild(p.getEl().dom);
47536             this.bodyEl.update("");
47537             this.bodyEl.dom.appendChild(p.getEl().dom);
47538             tempEl = null;
47539             this.updateTitle(p.getTitle());
47540             this.tabs = null;
47541             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
47542             this.setActivePanel(p);
47543         }
47544         panel.setRegion(null);
47545         if(this.activePanel == panel){
47546             this.activePanel = null;
47547         }
47548         if(this.config.autoDestroy !== false && preservePanel !== true){
47549             try{panel.destroy();}catch(e){}
47550         }
47551         this.fireEvent("panelremoved", this, panel);
47552         return panel;
47553     },
47554
47555     /**
47556      * Returns the TabPanel component used by this region
47557      * @return {Roo.TabPanel}
47558      */
47559     getTabs : function(){
47560         return this.tabs;
47561     },
47562
47563     createTool : function(parentEl, className){
47564         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
47565             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
47566         btn.addClassOnOver("x-layout-tools-button-over");
47567         return btn;
47568     }
47569 });/*
47570  * Based on:
47571  * Ext JS Library 1.1.1
47572  * Copyright(c) 2006-2007, Ext JS, LLC.
47573  *
47574  * Originally Released Under LGPL - original licence link has changed is not relivant.
47575  *
47576  * Fork - LGPL
47577  * <script type="text/javascript">
47578  */
47579  
47580
47581
47582 /**
47583  * @class Roo.SplitLayoutRegion
47584  * @extends Roo.LayoutRegion
47585  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
47586  */
47587 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
47588     this.cursor = cursor;
47589     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
47590 };
47591
47592 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
47593     splitTip : "Drag to resize.",
47594     collapsibleSplitTip : "Drag to resize. Double click to hide.",
47595     useSplitTips : false,
47596
47597     applyConfig : function(config){
47598         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
47599         if(config.split){
47600             if(!this.split){
47601                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
47602                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
47603                 /** The SplitBar for this region 
47604                 * @type Roo.SplitBar */
47605                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
47606                 this.split.on("moved", this.onSplitMove, this);
47607                 this.split.useShim = config.useShim === true;
47608                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
47609                 if(this.useSplitTips){
47610                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
47611                 }
47612                 if(config.collapsible){
47613                     this.split.el.on("dblclick", this.collapse,  this);
47614                 }
47615             }
47616             if(typeof config.minSize != "undefined"){
47617                 this.split.minSize = config.minSize;
47618             }
47619             if(typeof config.maxSize != "undefined"){
47620                 this.split.maxSize = config.maxSize;
47621             }
47622             if(config.hideWhenEmpty || config.hidden || config.collapsed){
47623                 this.hideSplitter();
47624             }
47625         }
47626     },
47627
47628     getHMaxSize : function(){
47629          var cmax = this.config.maxSize || 10000;
47630          var center = this.mgr.getRegion("center");
47631          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
47632     },
47633
47634     getVMaxSize : function(){
47635          var cmax = this.config.maxSize || 10000;
47636          var center = this.mgr.getRegion("center");
47637          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
47638     },
47639
47640     onSplitMove : function(split, newSize){
47641         this.fireEvent("resized", this, newSize);
47642     },
47643     
47644     /** 
47645      * Returns the {@link Roo.SplitBar} for this region.
47646      * @return {Roo.SplitBar}
47647      */
47648     getSplitBar : function(){
47649         return this.split;
47650     },
47651     
47652     hide : function(){
47653         this.hideSplitter();
47654         Roo.SplitLayoutRegion.superclass.hide.call(this);
47655     },
47656
47657     hideSplitter : function(){
47658         if(this.split){
47659             this.split.el.setLocation(-2000,-2000);
47660             this.split.el.hide();
47661         }
47662     },
47663
47664     show : function(){
47665         if(this.split){
47666             this.split.el.show();
47667         }
47668         Roo.SplitLayoutRegion.superclass.show.call(this);
47669     },
47670     
47671     beforeSlide: function(){
47672         if(Roo.isGecko){// firefox overflow auto bug workaround
47673             this.bodyEl.clip();
47674             if(this.tabs) this.tabs.bodyEl.clip();
47675             if(this.activePanel){
47676                 this.activePanel.getEl().clip();
47677                 
47678                 if(this.activePanel.beforeSlide){
47679                     this.activePanel.beforeSlide();
47680                 }
47681             }
47682         }
47683     },
47684     
47685     afterSlide : function(){
47686         if(Roo.isGecko){// firefox overflow auto bug workaround
47687             this.bodyEl.unclip();
47688             if(this.tabs) this.tabs.bodyEl.unclip();
47689             if(this.activePanel){
47690                 this.activePanel.getEl().unclip();
47691                 if(this.activePanel.afterSlide){
47692                     this.activePanel.afterSlide();
47693                 }
47694             }
47695         }
47696     },
47697
47698     initAutoHide : function(){
47699         if(this.autoHide !== false){
47700             if(!this.autoHideHd){
47701                 var st = new Roo.util.DelayedTask(this.slideIn, this);
47702                 this.autoHideHd = {
47703                     "mouseout": function(e){
47704                         if(!e.within(this.el, true)){
47705                             st.delay(500);
47706                         }
47707                     },
47708                     "mouseover" : function(e){
47709                         st.cancel();
47710                     },
47711                     scope : this
47712                 };
47713             }
47714             this.el.on(this.autoHideHd);
47715         }
47716     },
47717
47718     clearAutoHide : function(){
47719         if(this.autoHide !== false){
47720             this.el.un("mouseout", this.autoHideHd.mouseout);
47721             this.el.un("mouseover", this.autoHideHd.mouseover);
47722         }
47723     },
47724
47725     clearMonitor : function(){
47726         Roo.get(document).un("click", this.slideInIf, this);
47727     },
47728
47729     // these names are backwards but not changed for compat
47730     slideOut : function(){
47731         if(this.isSlid || this.el.hasActiveFx()){
47732             return;
47733         }
47734         this.isSlid = true;
47735         if(this.collapseBtn){
47736             this.collapseBtn.hide();
47737         }
47738         this.closeBtnState = this.closeBtn.getStyle('display');
47739         this.closeBtn.hide();
47740         if(this.stickBtn){
47741             this.stickBtn.show();
47742         }
47743         this.el.show();
47744         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
47745         this.beforeSlide();
47746         this.el.setStyle("z-index", 10001);
47747         this.el.slideIn(this.getSlideAnchor(), {
47748             callback: function(){
47749                 this.afterSlide();
47750                 this.initAutoHide();
47751                 Roo.get(document).on("click", this.slideInIf, this);
47752                 this.fireEvent("slideshow", this);
47753             },
47754             scope: this,
47755             block: true
47756         });
47757     },
47758
47759     afterSlideIn : function(){
47760         this.clearAutoHide();
47761         this.isSlid = false;
47762         this.clearMonitor();
47763         this.el.setStyle("z-index", "");
47764         if(this.collapseBtn){
47765             this.collapseBtn.show();
47766         }
47767         this.closeBtn.setStyle('display', this.closeBtnState);
47768         if(this.stickBtn){
47769             this.stickBtn.hide();
47770         }
47771         this.fireEvent("slidehide", this);
47772     },
47773
47774     slideIn : function(cb){
47775         if(!this.isSlid || this.el.hasActiveFx()){
47776             Roo.callback(cb);
47777             return;
47778         }
47779         this.isSlid = false;
47780         this.beforeSlide();
47781         this.el.slideOut(this.getSlideAnchor(), {
47782             callback: function(){
47783                 this.el.setLeftTop(-10000, -10000);
47784                 this.afterSlide();
47785                 this.afterSlideIn();
47786                 Roo.callback(cb);
47787             },
47788             scope: this,
47789             block: true
47790         });
47791     },
47792     
47793     slideInIf : function(e){
47794         if(!e.within(this.el)){
47795             this.slideIn();
47796         }
47797     },
47798
47799     animateCollapse : function(){
47800         this.beforeSlide();
47801         this.el.setStyle("z-index", 20000);
47802         var anchor = this.getSlideAnchor();
47803         this.el.slideOut(anchor, {
47804             callback : function(){
47805                 this.el.setStyle("z-index", "");
47806                 this.collapsedEl.slideIn(anchor, {duration:.3});
47807                 this.afterSlide();
47808                 this.el.setLocation(-10000,-10000);
47809                 this.el.hide();
47810                 this.fireEvent("collapsed", this);
47811             },
47812             scope: this,
47813             block: true
47814         });
47815     },
47816
47817     animateExpand : function(){
47818         this.beforeSlide();
47819         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
47820         this.el.setStyle("z-index", 20000);
47821         this.collapsedEl.hide({
47822             duration:.1
47823         });
47824         this.el.slideIn(this.getSlideAnchor(), {
47825             callback : function(){
47826                 this.el.setStyle("z-index", "");
47827                 this.afterSlide();
47828                 if(this.split){
47829                     this.split.el.show();
47830                 }
47831                 this.fireEvent("invalidated", this);
47832                 this.fireEvent("expanded", this);
47833             },
47834             scope: this,
47835             block: true
47836         });
47837     },
47838
47839     anchors : {
47840         "west" : "left",
47841         "east" : "right",
47842         "north" : "top",
47843         "south" : "bottom"
47844     },
47845
47846     sanchors : {
47847         "west" : "l",
47848         "east" : "r",
47849         "north" : "t",
47850         "south" : "b"
47851     },
47852
47853     canchors : {
47854         "west" : "tl-tr",
47855         "east" : "tr-tl",
47856         "north" : "tl-bl",
47857         "south" : "bl-tl"
47858     },
47859
47860     getAnchor : function(){
47861         return this.anchors[this.position];
47862     },
47863
47864     getCollapseAnchor : function(){
47865         return this.canchors[this.position];
47866     },
47867
47868     getSlideAnchor : function(){
47869         return this.sanchors[this.position];
47870     },
47871
47872     getAlignAdj : function(){
47873         var cm = this.cmargins;
47874         switch(this.position){
47875             case "west":
47876                 return [0, 0];
47877             break;
47878             case "east":
47879                 return [0, 0];
47880             break;
47881             case "north":
47882                 return [0, 0];
47883             break;
47884             case "south":
47885                 return [0, 0];
47886             break;
47887         }
47888     },
47889
47890     getExpandAdj : function(){
47891         var c = this.collapsedEl, cm = this.cmargins;
47892         switch(this.position){
47893             case "west":
47894                 return [-(cm.right+c.getWidth()+cm.left), 0];
47895             break;
47896             case "east":
47897                 return [cm.right+c.getWidth()+cm.left, 0];
47898             break;
47899             case "north":
47900                 return [0, -(cm.top+cm.bottom+c.getHeight())];
47901             break;
47902             case "south":
47903                 return [0, cm.top+cm.bottom+c.getHeight()];
47904             break;
47905         }
47906     }
47907 });/*
47908  * Based on:
47909  * Ext JS Library 1.1.1
47910  * Copyright(c) 2006-2007, Ext JS, LLC.
47911  *
47912  * Originally Released Under LGPL - original licence link has changed is not relivant.
47913  *
47914  * Fork - LGPL
47915  * <script type="text/javascript">
47916  */
47917 /*
47918  * These classes are private internal classes
47919  */
47920 Roo.CenterLayoutRegion = function(mgr, config){
47921     Roo.LayoutRegion.call(this, mgr, config, "center");
47922     this.visible = true;
47923     this.minWidth = config.minWidth || 20;
47924     this.minHeight = config.minHeight || 20;
47925 };
47926
47927 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
47928     hide : function(){
47929         // center panel can't be hidden
47930     },
47931     
47932     show : function(){
47933         // center panel can't be hidden
47934     },
47935     
47936     getMinWidth: function(){
47937         return this.minWidth;
47938     },
47939     
47940     getMinHeight: function(){
47941         return this.minHeight;
47942     }
47943 });
47944
47945
47946 Roo.NorthLayoutRegion = function(mgr, config){
47947     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
47948     if(this.split){
47949         this.split.placement = Roo.SplitBar.TOP;
47950         this.split.orientation = Roo.SplitBar.VERTICAL;
47951         this.split.el.addClass("x-layout-split-v");
47952     }
47953     var size = config.initialSize || config.height;
47954     if(typeof size != "undefined"){
47955         this.el.setHeight(size);
47956     }
47957 };
47958 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
47959     orientation: Roo.SplitBar.VERTICAL,
47960     getBox : function(){
47961         if(this.collapsed){
47962             return this.collapsedEl.getBox();
47963         }
47964         var box = this.el.getBox();
47965         if(this.split){
47966             box.height += this.split.el.getHeight();
47967         }
47968         return box;
47969     },
47970     
47971     updateBox : function(box){
47972         if(this.split && !this.collapsed){
47973             box.height -= this.split.el.getHeight();
47974             this.split.el.setLeft(box.x);
47975             this.split.el.setTop(box.y+box.height);
47976             this.split.el.setWidth(box.width);
47977         }
47978         if(this.collapsed){
47979             this.updateBody(box.width, null);
47980         }
47981         Roo.LayoutRegion.prototype.updateBox.call(this, box);
47982     }
47983 });
47984
47985 Roo.SouthLayoutRegion = function(mgr, config){
47986     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
47987     if(this.split){
47988         this.split.placement = Roo.SplitBar.BOTTOM;
47989         this.split.orientation = Roo.SplitBar.VERTICAL;
47990         this.split.el.addClass("x-layout-split-v");
47991     }
47992     var size = config.initialSize || config.height;
47993     if(typeof size != "undefined"){
47994         this.el.setHeight(size);
47995     }
47996 };
47997 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
47998     orientation: Roo.SplitBar.VERTICAL,
47999     getBox : function(){
48000         if(this.collapsed){
48001             return this.collapsedEl.getBox();
48002         }
48003         var box = this.el.getBox();
48004         if(this.split){
48005             var sh = this.split.el.getHeight();
48006             box.height += sh;
48007             box.y -= sh;
48008         }
48009         return box;
48010     },
48011     
48012     updateBox : function(box){
48013         if(this.split && !this.collapsed){
48014             var sh = this.split.el.getHeight();
48015             box.height -= sh;
48016             box.y += sh;
48017             this.split.el.setLeft(box.x);
48018             this.split.el.setTop(box.y-sh);
48019             this.split.el.setWidth(box.width);
48020         }
48021         if(this.collapsed){
48022             this.updateBody(box.width, null);
48023         }
48024         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48025     }
48026 });
48027
48028 Roo.EastLayoutRegion = function(mgr, config){
48029     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
48030     if(this.split){
48031         this.split.placement = Roo.SplitBar.RIGHT;
48032         this.split.orientation = Roo.SplitBar.HORIZONTAL;
48033         this.split.el.addClass("x-layout-split-h");
48034     }
48035     var size = config.initialSize || config.width;
48036     if(typeof size != "undefined"){
48037         this.el.setWidth(size);
48038     }
48039 };
48040 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
48041     orientation: Roo.SplitBar.HORIZONTAL,
48042     getBox : function(){
48043         if(this.collapsed){
48044             return this.collapsedEl.getBox();
48045         }
48046         var box = this.el.getBox();
48047         if(this.split){
48048             var sw = this.split.el.getWidth();
48049             box.width += sw;
48050             box.x -= sw;
48051         }
48052         return box;
48053     },
48054
48055     updateBox : function(box){
48056         if(this.split && !this.collapsed){
48057             var sw = this.split.el.getWidth();
48058             box.width -= sw;
48059             this.split.el.setLeft(box.x);
48060             this.split.el.setTop(box.y);
48061             this.split.el.setHeight(box.height);
48062             box.x += sw;
48063         }
48064         if(this.collapsed){
48065             this.updateBody(null, box.height);
48066         }
48067         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48068     }
48069 });
48070
48071 Roo.WestLayoutRegion = function(mgr, config){
48072     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
48073     if(this.split){
48074         this.split.placement = Roo.SplitBar.LEFT;
48075         this.split.orientation = Roo.SplitBar.HORIZONTAL;
48076         this.split.el.addClass("x-layout-split-h");
48077     }
48078     var size = config.initialSize || config.width;
48079     if(typeof size != "undefined"){
48080         this.el.setWidth(size);
48081     }
48082 };
48083 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
48084     orientation: Roo.SplitBar.HORIZONTAL,
48085     getBox : function(){
48086         if(this.collapsed){
48087             return this.collapsedEl.getBox();
48088         }
48089         var box = this.el.getBox();
48090         if(this.split){
48091             box.width += this.split.el.getWidth();
48092         }
48093         return box;
48094     },
48095     
48096     updateBox : function(box){
48097         if(this.split && !this.collapsed){
48098             var sw = this.split.el.getWidth();
48099             box.width -= sw;
48100             this.split.el.setLeft(box.x+box.width);
48101             this.split.el.setTop(box.y);
48102             this.split.el.setHeight(box.height);
48103         }
48104         if(this.collapsed){
48105             this.updateBody(null, box.height);
48106         }
48107         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48108     }
48109 });
48110 /*
48111  * Based on:
48112  * Ext JS Library 1.1.1
48113  * Copyright(c) 2006-2007, Ext JS, LLC.
48114  *
48115  * Originally Released Under LGPL - original licence link has changed is not relivant.
48116  *
48117  * Fork - LGPL
48118  * <script type="text/javascript">
48119  */
48120  
48121  
48122 /*
48123  * Private internal class for reading and applying state
48124  */
48125 Roo.LayoutStateManager = function(layout){
48126      // default empty state
48127      this.state = {
48128         north: {},
48129         south: {},
48130         east: {},
48131         west: {}       
48132     };
48133 };
48134
48135 Roo.LayoutStateManager.prototype = {
48136     init : function(layout, provider){
48137         this.provider = provider;
48138         var state = provider.get(layout.id+"-layout-state");
48139         if(state){
48140             var wasUpdating = layout.isUpdating();
48141             if(!wasUpdating){
48142                 layout.beginUpdate();
48143             }
48144             for(var key in state){
48145                 if(typeof state[key] != "function"){
48146                     var rstate = state[key];
48147                     var r = layout.getRegion(key);
48148                     if(r && rstate){
48149                         if(rstate.size){
48150                             r.resizeTo(rstate.size);
48151                         }
48152                         if(rstate.collapsed == true){
48153                             r.collapse(true);
48154                         }else{
48155                             r.expand(null, true);
48156                         }
48157                     }
48158                 }
48159             }
48160             if(!wasUpdating){
48161                 layout.endUpdate();
48162             }
48163             this.state = state; 
48164         }
48165         this.layout = layout;
48166         layout.on("regionresized", this.onRegionResized, this);
48167         layout.on("regioncollapsed", this.onRegionCollapsed, this);
48168         layout.on("regionexpanded", this.onRegionExpanded, this);
48169     },
48170     
48171     storeState : function(){
48172         this.provider.set(this.layout.id+"-layout-state", this.state);
48173     },
48174     
48175     onRegionResized : function(region, newSize){
48176         this.state[region.getPosition()].size = newSize;
48177         this.storeState();
48178     },
48179     
48180     onRegionCollapsed : function(region){
48181         this.state[region.getPosition()].collapsed = true;
48182         this.storeState();
48183     },
48184     
48185     onRegionExpanded : function(region){
48186         this.state[region.getPosition()].collapsed = false;
48187         this.storeState();
48188     }
48189 };/*
48190  * Based on:
48191  * Ext JS Library 1.1.1
48192  * Copyright(c) 2006-2007, Ext JS, LLC.
48193  *
48194  * Originally Released Under LGPL - original licence link has changed is not relivant.
48195  *
48196  * Fork - LGPL
48197  * <script type="text/javascript">
48198  */
48199 /**
48200  * @class Roo.ContentPanel
48201  * @extends Roo.util.Observable
48202  * A basic ContentPanel element.
48203  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
48204  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
48205  * @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
48206  * @cfg {Boolean}   closable      True if the panel can be closed/removed
48207  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
48208  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
48209  * @cfg {Toolbar}   toolbar       A toolbar for this panel
48210  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
48211  * @cfg {String} title          The title for this panel
48212  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
48213  * @cfg {String} url            Calls {@link #setUrl} with this value
48214  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
48215  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
48216  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
48217  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
48218
48219  * @constructor
48220  * Create a new ContentPanel.
48221  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
48222  * @param {String/Object} config A string to set only the title or a config object
48223  * @param {String} content (optional) Set the HTML content for this panel
48224  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
48225  */
48226 Roo.ContentPanel = function(el, config, content){
48227     
48228      
48229     /*
48230     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
48231         config = el;
48232         el = Roo.id();
48233     }
48234     if (config && config.parentLayout) { 
48235         el = config.parentLayout.el.createChild(); 
48236     }
48237     */
48238     if(el.autoCreate){ // xtype is available if this is called from factory
48239         config = el;
48240         el = Roo.id();
48241     }
48242     this.el = Roo.get(el);
48243     if(!this.el && config && config.autoCreate){
48244         if(typeof config.autoCreate == "object"){
48245             if(!config.autoCreate.id){
48246                 config.autoCreate.id = config.id||el;
48247             }
48248             this.el = Roo.DomHelper.append(document.body,
48249                         config.autoCreate, true);
48250         }else{
48251             this.el = Roo.DomHelper.append(document.body,
48252                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
48253         }
48254     }
48255     this.closable = false;
48256     this.loaded = false;
48257     this.active = false;
48258     if(typeof config == "string"){
48259         this.title = config;
48260     }else{
48261         Roo.apply(this, config);
48262     }
48263     
48264     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
48265         this.wrapEl = this.el.wrap();
48266         this.toolbar.container = this.el.insertSibling(false, 'before');
48267         this.toolbar = new Roo.Toolbar(this.toolbar);
48268     }
48269     
48270     // xtype created footer. - not sure if will work as we normally have to render first..
48271     if (this.footer && !this.footer.el && this.footer.xtype) {
48272         if (!this.wrapEl) {
48273             this.wrapEl = this.el.wrap();
48274         }
48275     
48276         this.footer.container = this.wrapEl.createChild();
48277          
48278         this.footer = Roo.factory(this.footer, Roo);
48279         
48280     }
48281     
48282     if(this.resizeEl){
48283         this.resizeEl = Roo.get(this.resizeEl, true);
48284     }else{
48285         this.resizeEl = this.el;
48286     }
48287     // handle view.xtype
48288     
48289     if (this.view && typeof(this.view.xtype) != 'undefined') {
48290         this.view.el = this.el.appendChild(document.createElement("div"));
48291         this.view = Roo.factory(this.view);
48292         this.view.render && this.view.render(false, ''); // render blank..
48293     }
48294     
48295     
48296     
48297     this.addEvents({
48298         /**
48299          * @event activate
48300          * Fires when this panel is activated. 
48301          * @param {Roo.ContentPanel} this
48302          */
48303         "activate" : true,
48304         /**
48305          * @event deactivate
48306          * Fires when this panel is activated. 
48307          * @param {Roo.ContentPanel} this
48308          */
48309         "deactivate" : true,
48310
48311         /**
48312          * @event resize
48313          * Fires when this panel is resized if fitToFrame is true.
48314          * @param {Roo.ContentPanel} this
48315          * @param {Number} width The width after any component adjustments
48316          * @param {Number} height The height after any component adjustments
48317          */
48318         "resize" : true,
48319         
48320          /**
48321          * @event render
48322          * Fires when this tab is created
48323          * @param {Roo.ContentPanel} this
48324          */
48325         "render" : true
48326         
48327         
48328         
48329     });
48330     if(this.autoScroll){
48331         this.resizeEl.setStyle("overflow", "auto");
48332     } else {
48333         // fix randome scrolling
48334         this.el.on('scroll', function() {
48335             Roo.log('fix random scolling');
48336             this.scrollTo('top',0); 
48337         });
48338     }
48339     content = content || this.content;
48340     if(content){
48341         this.setContent(content);
48342     }
48343     if(config && config.url){
48344         this.setUrl(this.url, this.params, this.loadOnce);
48345     }
48346     
48347     
48348     
48349     Roo.ContentPanel.superclass.constructor.call(this);
48350     
48351     this.fireEvent('render', this);
48352 };
48353
48354 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
48355     tabTip:'',
48356     setRegion : function(region){
48357         this.region = region;
48358         if(region){
48359            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
48360         }else{
48361            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
48362         } 
48363     },
48364     
48365     /**
48366      * Returns the toolbar for this Panel if one was configured. 
48367      * @return {Roo.Toolbar} 
48368      */
48369     getToolbar : function(){
48370         return this.toolbar;
48371     },
48372     
48373     setActiveState : function(active){
48374         this.active = active;
48375         if(!active){
48376             this.fireEvent("deactivate", this);
48377         }else{
48378             this.fireEvent("activate", this);
48379         }
48380     },
48381     /**
48382      * Updates this panel's element
48383      * @param {String} content The new content
48384      * @param {Boolean} loadScripts (optional) true to look for and process scripts
48385     */
48386     setContent : function(content, loadScripts){
48387         this.el.update(content, loadScripts);
48388     },
48389
48390     ignoreResize : function(w, h){
48391         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
48392             return true;
48393         }else{
48394             this.lastSize = {width: w, height: h};
48395             return false;
48396         }
48397     },
48398     /**
48399      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
48400      * @return {Roo.UpdateManager} The UpdateManager
48401      */
48402     getUpdateManager : function(){
48403         return this.el.getUpdateManager();
48404     },
48405      /**
48406      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
48407      * @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:
48408 <pre><code>
48409 panel.load({
48410     url: "your-url.php",
48411     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
48412     callback: yourFunction,
48413     scope: yourObject, //(optional scope)
48414     discardUrl: false,
48415     nocache: false,
48416     text: "Loading...",
48417     timeout: 30,
48418     scripts: false
48419 });
48420 </code></pre>
48421      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
48422      * 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.
48423      * @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}
48424      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
48425      * @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.
48426      * @return {Roo.ContentPanel} this
48427      */
48428     load : function(){
48429         var um = this.el.getUpdateManager();
48430         um.update.apply(um, arguments);
48431         return this;
48432     },
48433
48434
48435     /**
48436      * 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.
48437      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
48438      * @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)
48439      * @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)
48440      * @return {Roo.UpdateManager} The UpdateManager
48441      */
48442     setUrl : function(url, params, loadOnce){
48443         if(this.refreshDelegate){
48444             this.removeListener("activate", this.refreshDelegate);
48445         }
48446         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
48447         this.on("activate", this.refreshDelegate);
48448         return this.el.getUpdateManager();
48449     },
48450     
48451     _handleRefresh : function(url, params, loadOnce){
48452         if(!loadOnce || !this.loaded){
48453             var updater = this.el.getUpdateManager();
48454             updater.update(url, params, this._setLoaded.createDelegate(this));
48455         }
48456     },
48457     
48458     _setLoaded : function(){
48459         this.loaded = true;
48460     }, 
48461     
48462     /**
48463      * Returns this panel's id
48464      * @return {String} 
48465      */
48466     getId : function(){
48467         return this.el.id;
48468     },
48469     
48470     /** 
48471      * Returns this panel's element - used by regiosn to add.
48472      * @return {Roo.Element} 
48473      */
48474     getEl : function(){
48475         return this.wrapEl || this.el;
48476     },
48477     
48478     adjustForComponents : function(width, height)
48479     {
48480         Roo.log('adjustForComponents ');
48481         if(this.resizeEl != this.el){
48482             width -= this.el.getFrameWidth('lr');
48483             height -= this.el.getFrameWidth('tb');
48484         }
48485         if(this.toolbar){
48486             var te = this.toolbar.getEl();
48487             height -= te.getHeight();
48488             te.setWidth(width);
48489         }
48490         if(this.footer){
48491             var te = this.footer.getEl();
48492             Roo.log("footer:" + te.getHeight());
48493             
48494             height -= te.getHeight();
48495             te.setWidth(width);
48496         }
48497         
48498         
48499         if(this.adjustments){
48500             width += this.adjustments[0];
48501             height += this.adjustments[1];
48502         }
48503         return {"width": width, "height": height};
48504     },
48505     
48506     setSize : function(width, height){
48507         if(this.fitToFrame && !this.ignoreResize(width, height)){
48508             if(this.fitContainer && this.resizeEl != this.el){
48509                 this.el.setSize(width, height);
48510             }
48511             var size = this.adjustForComponents(width, height);
48512             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
48513             this.fireEvent('resize', this, size.width, size.height);
48514         }
48515     },
48516     
48517     /**
48518      * Returns this panel's title
48519      * @return {String} 
48520      */
48521     getTitle : function(){
48522         return this.title;
48523     },
48524     
48525     /**
48526      * Set this panel's title
48527      * @param {String} title
48528      */
48529     setTitle : function(title){
48530         this.title = title;
48531         if(this.region){
48532             this.region.updatePanelTitle(this, title);
48533         }
48534     },
48535     
48536     /**
48537      * Returns true is this panel was configured to be closable
48538      * @return {Boolean} 
48539      */
48540     isClosable : function(){
48541         return this.closable;
48542     },
48543     
48544     beforeSlide : function(){
48545         this.el.clip();
48546         this.resizeEl.clip();
48547     },
48548     
48549     afterSlide : function(){
48550         this.el.unclip();
48551         this.resizeEl.unclip();
48552     },
48553     
48554     /**
48555      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
48556      *   Will fail silently if the {@link #setUrl} method has not been called.
48557      *   This does not activate the panel, just updates its content.
48558      */
48559     refresh : function(){
48560         if(this.refreshDelegate){
48561            this.loaded = false;
48562            this.refreshDelegate();
48563         }
48564     },
48565     
48566     /**
48567      * Destroys this panel
48568      */
48569     destroy : function(){
48570         this.el.removeAllListeners();
48571         var tempEl = document.createElement("span");
48572         tempEl.appendChild(this.el.dom);
48573         tempEl.innerHTML = "";
48574         this.el.remove();
48575         this.el = null;
48576     },
48577     
48578     /**
48579      * form - if the content panel contains a form - this is a reference to it.
48580      * @type {Roo.form.Form}
48581      */
48582     form : false,
48583     /**
48584      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
48585      *    This contains a reference to it.
48586      * @type {Roo.View}
48587      */
48588     view : false,
48589     
48590       /**
48591      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
48592      * <pre><code>
48593
48594 layout.addxtype({
48595        xtype : 'Form',
48596        items: [ .... ]
48597    }
48598 );
48599
48600 </code></pre>
48601      * @param {Object} cfg Xtype definition of item to add.
48602      */
48603     
48604     addxtype : function(cfg) {
48605         // add form..
48606         if (cfg.xtype.match(/^Form$/)) {
48607             
48608             var el;
48609             //if (this.footer) {
48610             //    el = this.footer.container.insertSibling(false, 'before');
48611             //} else {
48612                 el = this.el.createChild();
48613             //}
48614
48615             this.form = new  Roo.form.Form(cfg);
48616             
48617             
48618             if ( this.form.allItems.length) this.form.render(el.dom);
48619             return this.form;
48620         }
48621         // should only have one of theses..
48622         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
48623             // views..
48624             cfg.el = this.el.appendChild(document.createElement("div"));
48625             // factory?
48626             
48627             var ret = new Roo.factory(cfg);
48628             ret.render && ret.render(false, ''); // render blank..
48629             this.view = ret;
48630             return ret;
48631         }
48632         return false;
48633     }
48634 });
48635
48636 /**
48637  * @class Roo.GridPanel
48638  * @extends Roo.ContentPanel
48639  * @constructor
48640  * Create a new GridPanel.
48641  * @param {Roo.grid.Grid} grid The grid for this panel
48642  * @param {String/Object} config A string to set only the panel's title, or a config object
48643  */
48644 Roo.GridPanel = function(grid, config){
48645     
48646   
48647     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
48648         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
48649         
48650     this.wrapper.dom.appendChild(grid.getGridEl().dom);
48651     
48652     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
48653     
48654     if(this.toolbar){
48655         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
48656     }
48657     // xtype created footer. - not sure if will work as we normally have to render first..
48658     if (this.footer && !this.footer.el && this.footer.xtype) {
48659         
48660         this.footer.container = this.grid.getView().getFooterPanel(true);
48661         this.footer.dataSource = this.grid.dataSource;
48662         this.footer = Roo.factory(this.footer, Roo);
48663         
48664     }
48665     
48666     grid.monitorWindowResize = false; // turn off autosizing
48667     grid.autoHeight = false;
48668     grid.autoWidth = false;
48669     this.grid = grid;
48670     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
48671 };
48672
48673 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
48674     getId : function(){
48675         return this.grid.id;
48676     },
48677     
48678     /**
48679      * Returns the grid for this panel
48680      * @return {Roo.grid.Grid} 
48681      */
48682     getGrid : function(){
48683         return this.grid;    
48684     },
48685     
48686     setSize : function(width, height){
48687         if(!this.ignoreResize(width, height)){
48688             var grid = this.grid;
48689             var size = this.adjustForComponents(width, height);
48690             grid.getGridEl().setSize(size.width, size.height);
48691             grid.autoSize();
48692         }
48693     },
48694     
48695     beforeSlide : function(){
48696         this.grid.getView().scroller.clip();
48697     },
48698     
48699     afterSlide : function(){
48700         this.grid.getView().scroller.unclip();
48701     },
48702     
48703     destroy : function(){
48704         this.grid.destroy();
48705         delete this.grid;
48706         Roo.GridPanel.superclass.destroy.call(this); 
48707     }
48708 });
48709
48710
48711 /**
48712  * @class Roo.NestedLayoutPanel
48713  * @extends Roo.ContentPanel
48714  * @constructor
48715  * Create a new NestedLayoutPanel.
48716  * 
48717  * 
48718  * @param {Roo.BorderLayout} layout The layout for this panel
48719  * @param {String/Object} config A string to set only the title or a config object
48720  */
48721 Roo.NestedLayoutPanel = function(layout, config)
48722 {
48723     // construct with only one argument..
48724     /* FIXME - implement nicer consturctors
48725     if (layout.layout) {
48726         config = layout;
48727         layout = config.layout;
48728         delete config.layout;
48729     }
48730     if (layout.xtype && !layout.getEl) {
48731         // then layout needs constructing..
48732         layout = Roo.factory(layout, Roo);
48733     }
48734     */
48735     
48736     
48737     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
48738     
48739     layout.monitorWindowResize = false; // turn off autosizing
48740     this.layout = layout;
48741     this.layout.getEl().addClass("x-layout-nested-layout");
48742     
48743     
48744     
48745     
48746 };
48747
48748 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
48749
48750     setSize : function(width, height){
48751         if(!this.ignoreResize(width, height)){
48752             var size = this.adjustForComponents(width, height);
48753             var el = this.layout.getEl();
48754             el.setSize(size.width, size.height);
48755             var touch = el.dom.offsetWidth;
48756             this.layout.layout();
48757             // ie requires a double layout on the first pass
48758             if(Roo.isIE && !this.initialized){
48759                 this.initialized = true;
48760                 this.layout.layout();
48761             }
48762         }
48763     },
48764     
48765     // activate all subpanels if not currently active..
48766     
48767     setActiveState : function(active){
48768         this.active = active;
48769         if(!active){
48770             this.fireEvent("deactivate", this);
48771             return;
48772         }
48773         
48774         this.fireEvent("activate", this);
48775         // not sure if this should happen before or after..
48776         if (!this.layout) {
48777             return; // should not happen..
48778         }
48779         var reg = false;
48780         for (var r in this.layout.regions) {
48781             reg = this.layout.getRegion(r);
48782             if (reg.getActivePanel()) {
48783                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
48784                 reg.setActivePanel(reg.getActivePanel());
48785                 continue;
48786             }
48787             if (!reg.panels.length) {
48788                 continue;
48789             }
48790             reg.showPanel(reg.getPanel(0));
48791         }
48792         
48793         
48794         
48795         
48796     },
48797     
48798     /**
48799      * Returns the nested BorderLayout for this panel
48800      * @return {Roo.BorderLayout} 
48801      */
48802     getLayout : function(){
48803         return this.layout;
48804     },
48805     
48806      /**
48807      * Adds a xtype elements to the layout of the nested panel
48808      * <pre><code>
48809
48810 panel.addxtype({
48811        xtype : 'ContentPanel',
48812        region: 'west',
48813        items: [ .... ]
48814    }
48815 );
48816
48817 panel.addxtype({
48818         xtype : 'NestedLayoutPanel',
48819         region: 'west',
48820         layout: {
48821            center: { },
48822            west: { }   
48823         },
48824         items : [ ... list of content panels or nested layout panels.. ]
48825    }
48826 );
48827 </code></pre>
48828      * @param {Object} cfg Xtype definition of item to add.
48829      */
48830     addxtype : function(cfg) {
48831         return this.layout.addxtype(cfg);
48832     
48833     }
48834 });
48835
48836 Roo.ScrollPanel = function(el, config, content){
48837     config = config || {};
48838     config.fitToFrame = true;
48839     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
48840     
48841     this.el.dom.style.overflow = "hidden";
48842     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
48843     this.el.removeClass("x-layout-inactive-content");
48844     this.el.on("mousewheel", this.onWheel, this);
48845
48846     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
48847     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
48848     up.unselectable(); down.unselectable();
48849     up.on("click", this.scrollUp, this);
48850     down.on("click", this.scrollDown, this);
48851     up.addClassOnOver("x-scroller-btn-over");
48852     down.addClassOnOver("x-scroller-btn-over");
48853     up.addClassOnClick("x-scroller-btn-click");
48854     down.addClassOnClick("x-scroller-btn-click");
48855     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
48856
48857     this.resizeEl = this.el;
48858     this.el = wrap; this.up = up; this.down = down;
48859 };
48860
48861 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
48862     increment : 100,
48863     wheelIncrement : 5,
48864     scrollUp : function(){
48865         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
48866     },
48867
48868     scrollDown : function(){
48869         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
48870     },
48871
48872     afterScroll : function(){
48873         var el = this.resizeEl;
48874         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
48875         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
48876         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
48877     },
48878
48879     setSize : function(){
48880         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
48881         this.afterScroll();
48882     },
48883
48884     onWheel : function(e){
48885         var d = e.getWheelDelta();
48886         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
48887         this.afterScroll();
48888         e.stopEvent();
48889     },
48890
48891     setContent : function(content, loadScripts){
48892         this.resizeEl.update(content, loadScripts);
48893     }
48894
48895 });
48896
48897
48898
48899
48900
48901
48902
48903
48904
48905 /**
48906  * @class Roo.TreePanel
48907  * @extends Roo.ContentPanel
48908  * @constructor
48909  * Create a new TreePanel. - defaults to fit/scoll contents.
48910  * @param {String/Object} config A string to set only the panel's title, or a config object
48911  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
48912  */
48913 Roo.TreePanel = function(config){
48914     var el = config.el;
48915     var tree = config.tree;
48916     delete config.tree; 
48917     delete config.el; // hopefull!
48918     
48919     // wrapper for IE7 strict & safari scroll issue
48920     
48921     var treeEl = el.createChild();
48922     config.resizeEl = treeEl;
48923     
48924     
48925     
48926     Roo.TreePanel.superclass.constructor.call(this, el, config);
48927  
48928  
48929     this.tree = new Roo.tree.TreePanel(treeEl , tree);
48930     //console.log(tree);
48931     this.on('activate', function()
48932     {
48933         if (this.tree.rendered) {
48934             return;
48935         }
48936         //console.log('render tree');
48937         this.tree.render();
48938     });
48939     // this should not be needed.. - it's actually the 'el' that resizes?
48940     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
48941     
48942     //this.on('resize',  function (cp, w, h) {
48943     //        this.tree.innerCt.setWidth(w);
48944     //        this.tree.innerCt.setHeight(h);
48945     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
48946     //});
48947
48948         
48949     
48950 };
48951
48952 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
48953     fitToFrame : true,
48954     autoScroll : true
48955 });
48956
48957
48958
48959
48960
48961
48962
48963
48964
48965
48966
48967 /*
48968  * Based on:
48969  * Ext JS Library 1.1.1
48970  * Copyright(c) 2006-2007, Ext JS, LLC.
48971  *
48972  * Originally Released Under LGPL - original licence link has changed is not relivant.
48973  *
48974  * Fork - LGPL
48975  * <script type="text/javascript">
48976  */
48977  
48978
48979 /**
48980  * @class Roo.ReaderLayout
48981  * @extends Roo.BorderLayout
48982  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
48983  * center region containing two nested regions (a top one for a list view and one for item preview below),
48984  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
48985  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
48986  * expedites the setup of the overall layout and regions for this common application style.
48987  * Example:
48988  <pre><code>
48989 var reader = new Roo.ReaderLayout();
48990 var CP = Roo.ContentPanel;  // shortcut for adding
48991
48992 reader.beginUpdate();
48993 reader.add("north", new CP("north", "North"));
48994 reader.add("west", new CP("west", {title: "West"}));
48995 reader.add("east", new CP("east", {title: "East"}));
48996
48997 reader.regions.listView.add(new CP("listView", "List"));
48998 reader.regions.preview.add(new CP("preview", "Preview"));
48999 reader.endUpdate();
49000 </code></pre>
49001 * @constructor
49002 * Create a new ReaderLayout
49003 * @param {Object} config Configuration options
49004 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
49005 * document.body if omitted)
49006 */
49007 Roo.ReaderLayout = function(config, renderTo){
49008     var c = config || {size:{}};
49009     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
49010         north: c.north !== false ? Roo.apply({
49011             split:false,
49012             initialSize: 32,
49013             titlebar: false
49014         }, c.north) : false,
49015         west: c.west !== false ? Roo.apply({
49016             split:true,
49017             initialSize: 200,
49018             minSize: 175,
49019             maxSize: 400,
49020             titlebar: true,
49021             collapsible: true,
49022             animate: true,
49023             margins:{left:5,right:0,bottom:5,top:5},
49024             cmargins:{left:5,right:5,bottom:5,top:5}
49025         }, c.west) : false,
49026         east: c.east !== false ? Roo.apply({
49027             split:true,
49028             initialSize: 200,
49029             minSize: 175,
49030             maxSize: 400,
49031             titlebar: true,
49032             collapsible: true,
49033             animate: true,
49034             margins:{left:0,right:5,bottom:5,top:5},
49035             cmargins:{left:5,right:5,bottom:5,top:5}
49036         }, c.east) : false,
49037         center: Roo.apply({
49038             tabPosition: 'top',
49039             autoScroll:false,
49040             closeOnTab: true,
49041             titlebar:false,
49042             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
49043         }, c.center)
49044     });
49045
49046     this.el.addClass('x-reader');
49047
49048     this.beginUpdate();
49049
49050     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
49051         south: c.preview !== false ? Roo.apply({
49052             split:true,
49053             initialSize: 200,
49054             minSize: 100,
49055             autoScroll:true,
49056             collapsible:true,
49057             titlebar: true,
49058             cmargins:{top:5,left:0, right:0, bottom:0}
49059         }, c.preview) : false,
49060         center: Roo.apply({
49061             autoScroll:false,
49062             titlebar:false,
49063             minHeight:200
49064         }, c.listView)
49065     });
49066     this.add('center', new Roo.NestedLayoutPanel(inner,
49067             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
49068
49069     this.endUpdate();
49070
49071     this.regions.preview = inner.getRegion('south');
49072     this.regions.listView = inner.getRegion('center');
49073 };
49074
49075 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
49076  * Based on:
49077  * Ext JS Library 1.1.1
49078  * Copyright(c) 2006-2007, Ext JS, LLC.
49079  *
49080  * Originally Released Under LGPL - original licence link has changed is not relivant.
49081  *
49082  * Fork - LGPL
49083  * <script type="text/javascript">
49084  */
49085  
49086 /**
49087  * @class Roo.grid.Grid
49088  * @extends Roo.util.Observable
49089  * This class represents the primary interface of a component based grid control.
49090  * <br><br>Usage:<pre><code>
49091  var grid = new Roo.grid.Grid("my-container-id", {
49092      ds: myDataStore,
49093      cm: myColModel,
49094      selModel: mySelectionModel,
49095      autoSizeColumns: true,
49096      monitorWindowResize: false,
49097      trackMouseOver: true
49098  });
49099  // set any options
49100  grid.render();
49101  * </code></pre>
49102  * <b>Common Problems:</b><br/>
49103  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
49104  * element will correct this<br/>
49105  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
49106  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
49107  * are unpredictable.<br/>
49108  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
49109  * grid to calculate dimensions/offsets.<br/>
49110   * @constructor
49111  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
49112  * The container MUST have some type of size defined for the grid to fill. The container will be
49113  * automatically set to position relative if it isn't already.
49114  * @param {Object} config A config object that sets properties on this grid.
49115  */
49116 Roo.grid.Grid = function(container, config){
49117         // initialize the container
49118         this.container = Roo.get(container);
49119         this.container.update("");
49120         this.container.setStyle("overflow", "hidden");
49121     this.container.addClass('x-grid-container');
49122
49123     this.id = this.container.id;
49124
49125     Roo.apply(this, config);
49126     // check and correct shorthanded configs
49127     if(this.ds){
49128         this.dataSource = this.ds;
49129         delete this.ds;
49130     }
49131     if(this.cm){
49132         this.colModel = this.cm;
49133         delete this.cm;
49134     }
49135     if(this.sm){
49136         this.selModel = this.sm;
49137         delete this.sm;
49138     }
49139
49140     if (this.selModel) {
49141         this.selModel = Roo.factory(this.selModel, Roo.grid);
49142         this.sm = this.selModel;
49143         this.sm.xmodule = this.xmodule || false;
49144     }
49145     if (typeof(this.colModel.config) == 'undefined') {
49146         this.colModel = new Roo.grid.ColumnModel(this.colModel);
49147         this.cm = this.colModel;
49148         this.cm.xmodule = this.xmodule || false;
49149     }
49150     if (this.dataSource) {
49151         this.dataSource= Roo.factory(this.dataSource, Roo.data);
49152         this.ds = this.dataSource;
49153         this.ds.xmodule = this.xmodule || false;
49154          
49155     }
49156     
49157     
49158     
49159     if(this.width){
49160         this.container.setWidth(this.width);
49161     }
49162
49163     if(this.height){
49164         this.container.setHeight(this.height);
49165     }
49166     /** @private */
49167         this.addEvents({
49168         // raw events
49169         /**
49170          * @event click
49171          * The raw click event for the entire grid.
49172          * @param {Roo.EventObject} e
49173          */
49174         "click" : true,
49175         /**
49176          * @event dblclick
49177          * The raw dblclick event for the entire grid.
49178          * @param {Roo.EventObject} e
49179          */
49180         "dblclick" : true,
49181         /**
49182          * @event contextmenu
49183          * The raw contextmenu event for the entire grid.
49184          * @param {Roo.EventObject} e
49185          */
49186         "contextmenu" : true,
49187         /**
49188          * @event mousedown
49189          * The raw mousedown event for the entire grid.
49190          * @param {Roo.EventObject} e
49191          */
49192         "mousedown" : true,
49193         /**
49194          * @event mouseup
49195          * The raw mouseup event for the entire grid.
49196          * @param {Roo.EventObject} e
49197          */
49198         "mouseup" : true,
49199         /**
49200          * @event mouseover
49201          * The raw mouseover event for the entire grid.
49202          * @param {Roo.EventObject} e
49203          */
49204         "mouseover" : true,
49205         /**
49206          * @event mouseout
49207          * The raw mouseout event for the entire grid.
49208          * @param {Roo.EventObject} e
49209          */
49210         "mouseout" : true,
49211         /**
49212          * @event keypress
49213          * The raw keypress event for the entire grid.
49214          * @param {Roo.EventObject} e
49215          */
49216         "keypress" : true,
49217         /**
49218          * @event keydown
49219          * The raw keydown event for the entire grid.
49220          * @param {Roo.EventObject} e
49221          */
49222         "keydown" : true,
49223
49224         // custom events
49225
49226         /**
49227          * @event cellclick
49228          * Fires when a cell is clicked
49229          * @param {Grid} this
49230          * @param {Number} rowIndex
49231          * @param {Number} columnIndex
49232          * @param {Roo.EventObject} e
49233          */
49234         "cellclick" : true,
49235         /**
49236          * @event celldblclick
49237          * Fires when a cell is double clicked
49238          * @param {Grid} this
49239          * @param {Number} rowIndex
49240          * @param {Number} columnIndex
49241          * @param {Roo.EventObject} e
49242          */
49243         "celldblclick" : true,
49244         /**
49245          * @event rowclick
49246          * Fires when a row is clicked
49247          * @param {Grid} this
49248          * @param {Number} rowIndex
49249          * @param {Roo.EventObject} e
49250          */
49251         "rowclick" : true,
49252         /**
49253          * @event rowdblclick
49254          * Fires when a row is double clicked
49255          * @param {Grid} this
49256          * @param {Number} rowIndex
49257          * @param {Roo.EventObject} e
49258          */
49259         "rowdblclick" : true,
49260         /**
49261          * @event headerclick
49262          * Fires when a header is clicked
49263          * @param {Grid} this
49264          * @param {Number} columnIndex
49265          * @param {Roo.EventObject} e
49266          */
49267         "headerclick" : true,
49268         /**
49269          * @event headerdblclick
49270          * Fires when a header cell is double clicked
49271          * @param {Grid} this
49272          * @param {Number} columnIndex
49273          * @param {Roo.EventObject} e
49274          */
49275         "headerdblclick" : true,
49276         /**
49277          * @event rowcontextmenu
49278          * Fires when a row is right clicked
49279          * @param {Grid} this
49280          * @param {Number} rowIndex
49281          * @param {Roo.EventObject} e
49282          */
49283         "rowcontextmenu" : true,
49284         /**
49285          * @event cellcontextmenu
49286          * Fires when a cell is right clicked
49287          * @param {Grid} this
49288          * @param {Number} rowIndex
49289          * @param {Number} cellIndex
49290          * @param {Roo.EventObject} e
49291          */
49292          "cellcontextmenu" : true,
49293         /**
49294          * @event headercontextmenu
49295          * Fires when a header is right clicked
49296          * @param {Grid} this
49297          * @param {Number} columnIndex
49298          * @param {Roo.EventObject} e
49299          */
49300         "headercontextmenu" : true,
49301         /**
49302          * @event bodyscroll
49303          * Fires when the body element is scrolled
49304          * @param {Number} scrollLeft
49305          * @param {Number} scrollTop
49306          */
49307         "bodyscroll" : true,
49308         /**
49309          * @event columnresize
49310          * Fires when the user resizes a column
49311          * @param {Number} columnIndex
49312          * @param {Number} newSize
49313          */
49314         "columnresize" : true,
49315         /**
49316          * @event columnmove
49317          * Fires when the user moves a column
49318          * @param {Number} oldIndex
49319          * @param {Number} newIndex
49320          */
49321         "columnmove" : true,
49322         /**
49323          * @event startdrag
49324          * Fires when row(s) start being dragged
49325          * @param {Grid} this
49326          * @param {Roo.GridDD} dd The drag drop object
49327          * @param {event} e The raw browser event
49328          */
49329         "startdrag" : true,
49330         /**
49331          * @event enddrag
49332          * Fires when a drag operation is complete
49333          * @param {Grid} this
49334          * @param {Roo.GridDD} dd The drag drop object
49335          * @param {event} e The raw browser event
49336          */
49337         "enddrag" : true,
49338         /**
49339          * @event dragdrop
49340          * Fires when dragged row(s) are dropped on a valid DD target
49341          * @param {Grid} this
49342          * @param {Roo.GridDD} dd The drag drop object
49343          * @param {String} targetId The target drag drop object
49344          * @param {event} e The raw browser event
49345          */
49346         "dragdrop" : true,
49347         /**
49348          * @event dragover
49349          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
49350          * @param {Grid} this
49351          * @param {Roo.GridDD} dd The drag drop object
49352          * @param {String} targetId The target drag drop object
49353          * @param {event} e The raw browser event
49354          */
49355         "dragover" : true,
49356         /**
49357          * @event dragenter
49358          *  Fires when the dragged row(s) first cross another DD target while being dragged
49359          * @param {Grid} this
49360          * @param {Roo.GridDD} dd The drag drop object
49361          * @param {String} targetId The target drag drop object
49362          * @param {event} e The raw browser event
49363          */
49364         "dragenter" : true,
49365         /**
49366          * @event dragout
49367          * Fires when the dragged row(s) leave another DD target while being dragged
49368          * @param {Grid} this
49369          * @param {Roo.GridDD} dd The drag drop object
49370          * @param {String} targetId The target drag drop object
49371          * @param {event} e The raw browser event
49372          */
49373         "dragout" : true,
49374         /**
49375          * @event rowclass
49376          * Fires when a row is rendered, so you can change add a style to it.
49377          * @param {GridView} gridview   The grid view
49378          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
49379          */
49380         'rowclass' : true,
49381
49382         /**
49383          * @event render
49384          * Fires when the grid is rendered
49385          * @param {Grid} grid
49386          */
49387         'render' : true
49388     });
49389
49390     Roo.grid.Grid.superclass.constructor.call(this);
49391 };
49392 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
49393     
49394     /**
49395      * @cfg {String} ddGroup - drag drop group.
49396      */
49397
49398     /**
49399      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
49400      */
49401     minColumnWidth : 25,
49402
49403     /**
49404      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
49405      * <b>on initial render.</b> It is more efficient to explicitly size the columns
49406      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
49407      */
49408     autoSizeColumns : false,
49409
49410     /**
49411      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
49412      */
49413     autoSizeHeaders : true,
49414
49415     /**
49416      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
49417      */
49418     monitorWindowResize : true,
49419
49420     /**
49421      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
49422      * rows measured to get a columns size. Default is 0 (all rows).
49423      */
49424     maxRowsToMeasure : 0,
49425
49426     /**
49427      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
49428      */
49429     trackMouseOver : true,
49430
49431     /**
49432     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
49433     */
49434     
49435     /**
49436     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
49437     */
49438     enableDragDrop : false,
49439     
49440     /**
49441     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
49442     */
49443     enableColumnMove : true,
49444     
49445     /**
49446     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
49447     */
49448     enableColumnHide : true,
49449     
49450     /**
49451     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
49452     */
49453     enableRowHeightSync : false,
49454     
49455     /**
49456     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
49457     */
49458     stripeRows : true,
49459     
49460     /**
49461     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
49462     */
49463     autoHeight : false,
49464
49465     /**
49466      * @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.
49467      */
49468     autoExpandColumn : false,
49469
49470     /**
49471     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
49472     * Default is 50.
49473     */
49474     autoExpandMin : 50,
49475
49476     /**
49477     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
49478     */
49479     autoExpandMax : 1000,
49480
49481     /**
49482     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
49483     */
49484     view : null,
49485
49486     /**
49487     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
49488     */
49489     loadMask : false,
49490     /**
49491     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
49492     */
49493     dropTarget: false,
49494     
49495    
49496     
49497     // private
49498     rendered : false,
49499
49500     /**
49501     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
49502     * of a fixed width. Default is false.
49503     */
49504     /**
49505     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
49506     */
49507     /**
49508      * Called once after all setup has been completed and the grid is ready to be rendered.
49509      * @return {Roo.grid.Grid} this
49510      */
49511     render : function()
49512     {
49513         var c = this.container;
49514         // try to detect autoHeight/width mode
49515         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
49516             this.autoHeight = true;
49517         }
49518         var view = this.getView();
49519         view.init(this);
49520
49521         c.on("click", this.onClick, this);
49522         c.on("dblclick", this.onDblClick, this);
49523         c.on("contextmenu", this.onContextMenu, this);
49524         c.on("keydown", this.onKeyDown, this);
49525
49526         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
49527
49528         this.getSelectionModel().init(this);
49529
49530         view.render();
49531
49532         if(this.loadMask){
49533             this.loadMask = new Roo.LoadMask(this.container,
49534                     Roo.apply({store:this.dataSource}, this.loadMask));
49535         }
49536         
49537         
49538         if (this.toolbar && this.toolbar.xtype) {
49539             this.toolbar.container = this.getView().getHeaderPanel(true);
49540             this.toolbar = new Roo.Toolbar(this.toolbar);
49541         }
49542         if (this.footer && this.footer.xtype) {
49543             this.footer.dataSource = this.getDataSource();
49544             this.footer.container = this.getView().getFooterPanel(true);
49545             this.footer = Roo.factory(this.footer, Roo);
49546         }
49547         if (this.dropTarget && this.dropTarget.xtype) {
49548             delete this.dropTarget.xtype;
49549             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
49550         }
49551         
49552         
49553         this.rendered = true;
49554         this.fireEvent('render', this);
49555         return this;
49556     },
49557
49558         /**
49559          * Reconfigures the grid to use a different Store and Column Model.
49560          * The View will be bound to the new objects and refreshed.
49561          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
49562          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
49563          */
49564     reconfigure : function(dataSource, colModel){
49565         if(this.loadMask){
49566             this.loadMask.destroy();
49567             this.loadMask = new Roo.LoadMask(this.container,
49568                     Roo.apply({store:dataSource}, this.loadMask));
49569         }
49570         this.view.bind(dataSource, colModel);
49571         this.dataSource = dataSource;
49572         this.colModel = colModel;
49573         this.view.refresh(true);
49574     },
49575
49576     // private
49577     onKeyDown : function(e){
49578         this.fireEvent("keydown", e);
49579     },
49580
49581     /**
49582      * Destroy this grid.
49583      * @param {Boolean} removeEl True to remove the element
49584      */
49585     destroy : function(removeEl, keepListeners){
49586         if(this.loadMask){
49587             this.loadMask.destroy();
49588         }
49589         var c = this.container;
49590         c.removeAllListeners();
49591         this.view.destroy();
49592         this.colModel.purgeListeners();
49593         if(!keepListeners){
49594             this.purgeListeners();
49595         }
49596         c.update("");
49597         if(removeEl === true){
49598             c.remove();
49599         }
49600     },
49601
49602     // private
49603     processEvent : function(name, e){
49604         this.fireEvent(name, e);
49605         var t = e.getTarget();
49606         var v = this.view;
49607         var header = v.findHeaderIndex(t);
49608         if(header !== false){
49609             this.fireEvent("header" + name, this, header, e);
49610         }else{
49611             var row = v.findRowIndex(t);
49612             var cell = v.findCellIndex(t);
49613             if(row !== false){
49614                 this.fireEvent("row" + name, this, row, e);
49615                 if(cell !== false){
49616                     this.fireEvent("cell" + name, this, row, cell, e);
49617                 }
49618             }
49619         }
49620     },
49621
49622     // private
49623     onClick : function(e){
49624         this.processEvent("click", e);
49625     },
49626
49627     // private
49628     onContextMenu : function(e, t){
49629         this.processEvent("contextmenu", e);
49630     },
49631
49632     // private
49633     onDblClick : function(e){
49634         this.processEvent("dblclick", e);
49635     },
49636
49637     // private
49638     walkCells : function(row, col, step, fn, scope){
49639         var cm = this.colModel, clen = cm.getColumnCount();
49640         var ds = this.dataSource, rlen = ds.getCount(), first = true;
49641         if(step < 0){
49642             if(col < 0){
49643                 row--;
49644                 first = false;
49645             }
49646             while(row >= 0){
49647                 if(!first){
49648                     col = clen-1;
49649                 }
49650                 first = false;
49651                 while(col >= 0){
49652                     if(fn.call(scope || this, row, col, cm) === true){
49653                         return [row, col];
49654                     }
49655                     col--;
49656                 }
49657                 row--;
49658             }
49659         } else {
49660             if(col >= clen){
49661                 row++;
49662                 first = false;
49663             }
49664             while(row < rlen){
49665                 if(!first){
49666                     col = 0;
49667                 }
49668                 first = false;
49669                 while(col < clen){
49670                     if(fn.call(scope || this, row, col, cm) === true){
49671                         return [row, col];
49672                     }
49673                     col++;
49674                 }
49675                 row++;
49676             }
49677         }
49678         return null;
49679     },
49680
49681     // private
49682     getSelections : function(){
49683         return this.selModel.getSelections();
49684     },
49685
49686     /**
49687      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
49688      * but if manual update is required this method will initiate it.
49689      */
49690     autoSize : function(){
49691         if(this.rendered){
49692             this.view.layout();
49693             if(this.view.adjustForScroll){
49694                 this.view.adjustForScroll();
49695             }
49696         }
49697     },
49698
49699     /**
49700      * Returns the grid's underlying element.
49701      * @return {Element} The element
49702      */
49703     getGridEl : function(){
49704         return this.container;
49705     },
49706
49707     // private for compatibility, overridden by editor grid
49708     stopEditing : function(){},
49709
49710     /**
49711      * Returns the grid's SelectionModel.
49712      * @return {SelectionModel}
49713      */
49714     getSelectionModel : function(){
49715         if(!this.selModel){
49716             this.selModel = new Roo.grid.RowSelectionModel();
49717         }
49718         return this.selModel;
49719     },
49720
49721     /**
49722      * Returns the grid's DataSource.
49723      * @return {DataSource}
49724      */
49725     getDataSource : function(){
49726         return this.dataSource;
49727     },
49728
49729     /**
49730      * Returns the grid's ColumnModel.
49731      * @return {ColumnModel}
49732      */
49733     getColumnModel : function(){
49734         return this.colModel;
49735     },
49736
49737     /**
49738      * Returns the grid's GridView object.
49739      * @return {GridView}
49740      */
49741     getView : function(){
49742         if(!this.view){
49743             this.view = new Roo.grid.GridView(this.viewConfig);
49744         }
49745         return this.view;
49746     },
49747     /**
49748      * Called to get grid's drag proxy text, by default returns this.ddText.
49749      * @return {String}
49750      */
49751     getDragDropText : function(){
49752         var count = this.selModel.getCount();
49753         return String.format(this.ddText, count, count == 1 ? '' : 's');
49754     }
49755 });
49756 /**
49757  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
49758  * %0 is replaced with the number of selected rows.
49759  * @type String
49760  */
49761 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
49762  * Based on:
49763  * Ext JS Library 1.1.1
49764  * Copyright(c) 2006-2007, Ext JS, LLC.
49765  *
49766  * Originally Released Under LGPL - original licence link has changed is not relivant.
49767  *
49768  * Fork - LGPL
49769  * <script type="text/javascript">
49770  */
49771  
49772 Roo.grid.AbstractGridView = function(){
49773         this.grid = null;
49774         
49775         this.events = {
49776             "beforerowremoved" : true,
49777             "beforerowsinserted" : true,
49778             "beforerefresh" : true,
49779             "rowremoved" : true,
49780             "rowsinserted" : true,
49781             "rowupdated" : true,
49782             "refresh" : true
49783         };
49784     Roo.grid.AbstractGridView.superclass.constructor.call(this);
49785 };
49786
49787 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
49788     rowClass : "x-grid-row",
49789     cellClass : "x-grid-cell",
49790     tdClass : "x-grid-td",
49791     hdClass : "x-grid-hd",
49792     splitClass : "x-grid-hd-split",
49793     
49794         init: function(grid){
49795         this.grid = grid;
49796                 var cid = this.grid.getGridEl().id;
49797         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
49798         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
49799         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
49800         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
49801         },
49802         
49803         getColumnRenderers : function(){
49804         var renderers = [];
49805         var cm = this.grid.colModel;
49806         var colCount = cm.getColumnCount();
49807         for(var i = 0; i < colCount; i++){
49808             renderers[i] = cm.getRenderer(i);
49809         }
49810         return renderers;
49811     },
49812     
49813     getColumnIds : function(){
49814         var ids = [];
49815         var cm = this.grid.colModel;
49816         var colCount = cm.getColumnCount();
49817         for(var i = 0; i < colCount; i++){
49818             ids[i] = cm.getColumnId(i);
49819         }
49820         return ids;
49821     },
49822     
49823     getDataIndexes : function(){
49824         if(!this.indexMap){
49825             this.indexMap = this.buildIndexMap();
49826         }
49827         return this.indexMap.colToData;
49828     },
49829     
49830     getColumnIndexByDataIndex : function(dataIndex){
49831         if(!this.indexMap){
49832             this.indexMap = this.buildIndexMap();
49833         }
49834         return this.indexMap.dataToCol[dataIndex];
49835     },
49836     
49837     /**
49838      * Set a css style for a column dynamically. 
49839      * @param {Number} colIndex The index of the column
49840      * @param {String} name The css property name
49841      * @param {String} value The css value
49842      */
49843     setCSSStyle : function(colIndex, name, value){
49844         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
49845         Roo.util.CSS.updateRule(selector, name, value);
49846     },
49847     
49848     generateRules : function(cm){
49849         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
49850         Roo.util.CSS.removeStyleSheet(rulesId);
49851         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49852             var cid = cm.getColumnId(i);
49853             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
49854                          this.tdSelector, cid, " {\n}\n",
49855                          this.hdSelector, cid, " {\n}\n",
49856                          this.splitSelector, cid, " {\n}\n");
49857         }
49858         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49859     }
49860 });/*
49861  * Based on:
49862  * Ext JS Library 1.1.1
49863  * Copyright(c) 2006-2007, Ext JS, LLC.
49864  *
49865  * Originally Released Under LGPL - original licence link has changed is not relivant.
49866  *
49867  * Fork - LGPL
49868  * <script type="text/javascript">
49869  */
49870
49871 // private
49872 // This is a support class used internally by the Grid components
49873 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
49874     this.grid = grid;
49875     this.view = grid.getView();
49876     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
49877     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
49878     if(hd2){
49879         this.setHandleElId(Roo.id(hd));
49880         this.setOuterHandleElId(Roo.id(hd2));
49881     }
49882     this.scroll = false;
49883 };
49884 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
49885     maxDragWidth: 120,
49886     getDragData : function(e){
49887         var t = Roo.lib.Event.getTarget(e);
49888         var h = this.view.findHeaderCell(t);
49889         if(h){
49890             return {ddel: h.firstChild, header:h};
49891         }
49892         return false;
49893     },
49894
49895     onInitDrag : function(e){
49896         this.view.headersDisabled = true;
49897         var clone = this.dragData.ddel.cloneNode(true);
49898         clone.id = Roo.id();
49899         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
49900         this.proxy.update(clone);
49901         return true;
49902     },
49903
49904     afterValidDrop : function(){
49905         var v = this.view;
49906         setTimeout(function(){
49907             v.headersDisabled = false;
49908         }, 50);
49909     },
49910
49911     afterInvalidDrop : function(){
49912         var v = this.view;
49913         setTimeout(function(){
49914             v.headersDisabled = false;
49915         }, 50);
49916     }
49917 });
49918 /*
49919  * Based on:
49920  * Ext JS Library 1.1.1
49921  * Copyright(c) 2006-2007, Ext JS, LLC.
49922  *
49923  * Originally Released Under LGPL - original licence link has changed is not relivant.
49924  *
49925  * Fork - LGPL
49926  * <script type="text/javascript">
49927  */
49928 // private
49929 // This is a support class used internally by the Grid components
49930 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
49931     this.grid = grid;
49932     this.view = grid.getView();
49933     // split the proxies so they don't interfere with mouse events
49934     this.proxyTop = Roo.DomHelper.append(document.body, {
49935         cls:"col-move-top", html:"&#160;"
49936     }, true);
49937     this.proxyBottom = Roo.DomHelper.append(document.body, {
49938         cls:"col-move-bottom", html:"&#160;"
49939     }, true);
49940     this.proxyTop.hide = this.proxyBottom.hide = function(){
49941         this.setLeftTop(-100,-100);
49942         this.setStyle("visibility", "hidden");
49943     };
49944     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
49945     // temporarily disabled
49946     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
49947     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
49948 };
49949 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
49950     proxyOffsets : [-4, -9],
49951     fly: Roo.Element.fly,
49952
49953     getTargetFromEvent : function(e){
49954         var t = Roo.lib.Event.getTarget(e);
49955         var cindex = this.view.findCellIndex(t);
49956         if(cindex !== false){
49957             return this.view.getHeaderCell(cindex);
49958         }
49959         return null;
49960     },
49961
49962     nextVisible : function(h){
49963         var v = this.view, cm = this.grid.colModel;
49964         h = h.nextSibling;
49965         while(h){
49966             if(!cm.isHidden(v.getCellIndex(h))){
49967                 return h;
49968             }
49969             h = h.nextSibling;
49970         }
49971         return null;
49972     },
49973
49974     prevVisible : function(h){
49975         var v = this.view, cm = this.grid.colModel;
49976         h = h.prevSibling;
49977         while(h){
49978             if(!cm.isHidden(v.getCellIndex(h))){
49979                 return h;
49980             }
49981             h = h.prevSibling;
49982         }
49983         return null;
49984     },
49985
49986     positionIndicator : function(h, n, e){
49987         var x = Roo.lib.Event.getPageX(e);
49988         var r = Roo.lib.Dom.getRegion(n.firstChild);
49989         var px, pt, py = r.top + this.proxyOffsets[1];
49990         if((r.right - x) <= (r.right-r.left)/2){
49991             px = r.right+this.view.borderWidth;
49992             pt = "after";
49993         }else{
49994             px = r.left;
49995             pt = "before";
49996         }
49997         var oldIndex = this.view.getCellIndex(h);
49998         var newIndex = this.view.getCellIndex(n);
49999
50000         if(this.grid.colModel.isFixed(newIndex)){
50001             return false;
50002         }
50003
50004         var locked = this.grid.colModel.isLocked(newIndex);
50005
50006         if(pt == "after"){
50007             newIndex++;
50008         }
50009         if(oldIndex < newIndex){
50010             newIndex--;
50011         }
50012         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
50013             return false;
50014         }
50015         px +=  this.proxyOffsets[0];
50016         this.proxyTop.setLeftTop(px, py);
50017         this.proxyTop.show();
50018         if(!this.bottomOffset){
50019             this.bottomOffset = this.view.mainHd.getHeight();
50020         }
50021         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
50022         this.proxyBottom.show();
50023         return pt;
50024     },
50025
50026     onNodeEnter : function(n, dd, e, data){
50027         if(data.header != n){
50028             this.positionIndicator(data.header, n, e);
50029         }
50030     },
50031
50032     onNodeOver : function(n, dd, e, data){
50033         var result = false;
50034         if(data.header != n){
50035             result = this.positionIndicator(data.header, n, e);
50036         }
50037         if(!result){
50038             this.proxyTop.hide();
50039             this.proxyBottom.hide();
50040         }
50041         return result ? this.dropAllowed : this.dropNotAllowed;
50042     },
50043
50044     onNodeOut : function(n, dd, e, data){
50045         this.proxyTop.hide();
50046         this.proxyBottom.hide();
50047     },
50048
50049     onNodeDrop : function(n, dd, e, data){
50050         var h = data.header;
50051         if(h != n){
50052             var cm = this.grid.colModel;
50053             var x = Roo.lib.Event.getPageX(e);
50054             var r = Roo.lib.Dom.getRegion(n.firstChild);
50055             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
50056             var oldIndex = this.view.getCellIndex(h);
50057             var newIndex = this.view.getCellIndex(n);
50058             var locked = cm.isLocked(newIndex);
50059             if(pt == "after"){
50060                 newIndex++;
50061             }
50062             if(oldIndex < newIndex){
50063                 newIndex--;
50064             }
50065             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
50066                 return false;
50067             }
50068             cm.setLocked(oldIndex, locked, true);
50069             cm.moveColumn(oldIndex, newIndex);
50070             this.grid.fireEvent("columnmove", oldIndex, newIndex);
50071             return true;
50072         }
50073         return false;
50074     }
50075 });
50076 /*
50077  * Based on:
50078  * Ext JS Library 1.1.1
50079  * Copyright(c) 2006-2007, Ext JS, LLC.
50080  *
50081  * Originally Released Under LGPL - original licence link has changed is not relivant.
50082  *
50083  * Fork - LGPL
50084  * <script type="text/javascript">
50085  */
50086   
50087 /**
50088  * @class Roo.grid.GridView
50089  * @extends Roo.util.Observable
50090  *
50091  * @constructor
50092  * @param {Object} config
50093  */
50094 Roo.grid.GridView = function(config){
50095     Roo.grid.GridView.superclass.constructor.call(this);
50096     this.el = null;
50097
50098     Roo.apply(this, config);
50099 };
50100
50101 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
50102
50103     unselectable :  'unselectable="on"',
50104     unselectableCls :  'x-unselectable',
50105     
50106     
50107     rowClass : "x-grid-row",
50108
50109     cellClass : "x-grid-col",
50110
50111     tdClass : "x-grid-td",
50112
50113     hdClass : "x-grid-hd",
50114
50115     splitClass : "x-grid-split",
50116
50117     sortClasses : ["sort-asc", "sort-desc"],
50118
50119     enableMoveAnim : false,
50120
50121     hlColor: "C3DAF9",
50122
50123     dh : Roo.DomHelper,
50124
50125     fly : Roo.Element.fly,
50126
50127     css : Roo.util.CSS,
50128
50129     borderWidth: 1,
50130
50131     splitOffset: 3,
50132
50133     scrollIncrement : 22,
50134
50135     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
50136
50137     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
50138
50139     bind : function(ds, cm){
50140         if(this.ds){
50141             this.ds.un("load", this.onLoad, this);
50142             this.ds.un("datachanged", this.onDataChange, this);
50143             this.ds.un("add", this.onAdd, this);
50144             this.ds.un("remove", this.onRemove, this);
50145             this.ds.un("update", this.onUpdate, this);
50146             this.ds.un("clear", this.onClear, this);
50147         }
50148         if(ds){
50149             ds.on("load", this.onLoad, this);
50150             ds.on("datachanged", this.onDataChange, this);
50151             ds.on("add", this.onAdd, this);
50152             ds.on("remove", this.onRemove, this);
50153             ds.on("update", this.onUpdate, this);
50154             ds.on("clear", this.onClear, this);
50155         }
50156         this.ds = ds;
50157
50158         if(this.cm){
50159             this.cm.un("widthchange", this.onColWidthChange, this);
50160             this.cm.un("headerchange", this.onHeaderChange, this);
50161             this.cm.un("hiddenchange", this.onHiddenChange, this);
50162             this.cm.un("columnmoved", this.onColumnMove, this);
50163             this.cm.un("columnlockchange", this.onColumnLock, this);
50164         }
50165         if(cm){
50166             this.generateRules(cm);
50167             cm.on("widthchange", this.onColWidthChange, this);
50168             cm.on("headerchange", this.onHeaderChange, this);
50169             cm.on("hiddenchange", this.onHiddenChange, this);
50170             cm.on("columnmoved", this.onColumnMove, this);
50171             cm.on("columnlockchange", this.onColumnLock, this);
50172         }
50173         this.cm = cm;
50174     },
50175
50176     init: function(grid){
50177         Roo.grid.GridView.superclass.init.call(this, grid);
50178
50179         this.bind(grid.dataSource, grid.colModel);
50180
50181         grid.on("headerclick", this.handleHeaderClick, this);
50182
50183         if(grid.trackMouseOver){
50184             grid.on("mouseover", this.onRowOver, this);
50185             grid.on("mouseout", this.onRowOut, this);
50186         }
50187         grid.cancelTextSelection = function(){};
50188         this.gridId = grid.id;
50189
50190         var tpls = this.templates || {};
50191
50192         if(!tpls.master){
50193             tpls.master = new Roo.Template(
50194                '<div class="x-grid" hidefocus="true">',
50195                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
50196                   '<div class="x-grid-topbar"></div>',
50197                   '<div class="x-grid-scroller"><div></div></div>',
50198                   '<div class="x-grid-locked">',
50199                       '<div class="x-grid-header">{lockedHeader}</div>',
50200                       '<div class="x-grid-body">{lockedBody}</div>',
50201                   "</div>",
50202                   '<div class="x-grid-viewport">',
50203                       '<div class="x-grid-header">{header}</div>',
50204                       '<div class="x-grid-body">{body}</div>',
50205                   "</div>",
50206                   '<div class="x-grid-bottombar"></div>',
50207                  
50208                   '<div class="x-grid-resize-proxy">&#160;</div>',
50209                "</div>"
50210             );
50211             tpls.master.disableformats = true;
50212         }
50213
50214         if(!tpls.header){
50215             tpls.header = new Roo.Template(
50216                '<table border="0" cellspacing="0" cellpadding="0">',
50217                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
50218                "</table>{splits}"
50219             );
50220             tpls.header.disableformats = true;
50221         }
50222         tpls.header.compile();
50223
50224         if(!tpls.hcell){
50225             tpls.hcell = new Roo.Template(
50226                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
50227                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
50228                 "</div></td>"
50229              );
50230              tpls.hcell.disableFormats = true;
50231         }
50232         tpls.hcell.compile();
50233
50234         if(!tpls.hsplit){
50235             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
50236                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
50237             tpls.hsplit.disableFormats = true;
50238         }
50239         tpls.hsplit.compile();
50240
50241         if(!tpls.body){
50242             tpls.body = new Roo.Template(
50243                '<table border="0" cellspacing="0" cellpadding="0">',
50244                "<tbody>{rows}</tbody>",
50245                "</table>"
50246             );
50247             tpls.body.disableFormats = true;
50248         }
50249         tpls.body.compile();
50250
50251         if(!tpls.row){
50252             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
50253             tpls.row.disableFormats = true;
50254         }
50255         tpls.row.compile();
50256
50257         if(!tpls.cell){
50258             tpls.cell = new Roo.Template(
50259                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
50260                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
50261                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
50262                 "</td>"
50263             );
50264             tpls.cell.disableFormats = true;
50265         }
50266         tpls.cell.compile();
50267
50268         this.templates = tpls;
50269     },
50270
50271     // remap these for backwards compat
50272     onColWidthChange : function(){
50273         this.updateColumns.apply(this, arguments);
50274     },
50275     onHeaderChange : function(){
50276         this.updateHeaders.apply(this, arguments);
50277     }, 
50278     onHiddenChange : function(){
50279         this.handleHiddenChange.apply(this, arguments);
50280     },
50281     onColumnMove : function(){
50282         this.handleColumnMove.apply(this, arguments);
50283     },
50284     onColumnLock : function(){
50285         this.handleLockChange.apply(this, arguments);
50286     },
50287
50288     onDataChange : function(){
50289         this.refresh();
50290         this.updateHeaderSortState();
50291     },
50292
50293     onClear : function(){
50294         this.refresh();
50295     },
50296
50297     onUpdate : function(ds, record){
50298         this.refreshRow(record);
50299     },
50300
50301     refreshRow : function(record){
50302         var ds = this.ds, index;
50303         if(typeof record == 'number'){
50304             index = record;
50305             record = ds.getAt(index);
50306         }else{
50307             index = ds.indexOf(record);
50308         }
50309         this.insertRows(ds, index, index, true);
50310         this.onRemove(ds, record, index+1, true);
50311         this.syncRowHeights(index, index);
50312         this.layout();
50313         this.fireEvent("rowupdated", this, index, record);
50314     },
50315
50316     onAdd : function(ds, records, index){
50317         this.insertRows(ds, index, index + (records.length-1));
50318     },
50319
50320     onRemove : function(ds, record, index, isUpdate){
50321         if(isUpdate !== true){
50322             this.fireEvent("beforerowremoved", this, index, record);
50323         }
50324         var bt = this.getBodyTable(), lt = this.getLockedTable();
50325         if(bt.rows[index]){
50326             bt.firstChild.removeChild(bt.rows[index]);
50327         }
50328         if(lt.rows[index]){
50329             lt.firstChild.removeChild(lt.rows[index]);
50330         }
50331         if(isUpdate !== true){
50332             this.stripeRows(index);
50333             this.syncRowHeights(index, index);
50334             this.layout();
50335             this.fireEvent("rowremoved", this, index, record);
50336         }
50337     },
50338
50339     onLoad : function(){
50340         this.scrollToTop();
50341     },
50342
50343     /**
50344      * Scrolls the grid to the top
50345      */
50346     scrollToTop : function(){
50347         if(this.scroller){
50348             this.scroller.dom.scrollTop = 0;
50349             this.syncScroll();
50350         }
50351     },
50352
50353     /**
50354      * Gets a panel in the header of the grid that can be used for toolbars etc.
50355      * After modifying the contents of this panel a call to grid.autoSize() may be
50356      * required to register any changes in size.
50357      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
50358      * @return Roo.Element
50359      */
50360     getHeaderPanel : function(doShow){
50361         if(doShow){
50362             this.headerPanel.show();
50363         }
50364         return this.headerPanel;
50365     },
50366
50367     /**
50368      * Gets a panel in the footer of the grid that can be used for toolbars etc.
50369      * After modifying the contents of this panel a call to grid.autoSize() may be
50370      * required to register any changes in size.
50371      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
50372      * @return Roo.Element
50373      */
50374     getFooterPanel : function(doShow){
50375         if(doShow){
50376             this.footerPanel.show();
50377         }
50378         return this.footerPanel;
50379     },
50380
50381     initElements : function(){
50382         var E = Roo.Element;
50383         var el = this.grid.getGridEl().dom.firstChild;
50384         var cs = el.childNodes;
50385
50386         this.el = new E(el);
50387         
50388          this.focusEl = new E(el.firstChild);
50389         this.focusEl.swallowEvent("click", true);
50390         
50391         this.headerPanel = new E(cs[1]);
50392         this.headerPanel.enableDisplayMode("block");
50393
50394         this.scroller = new E(cs[2]);
50395         this.scrollSizer = new E(this.scroller.dom.firstChild);
50396
50397         this.lockedWrap = new E(cs[3]);
50398         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
50399         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
50400
50401         this.mainWrap = new E(cs[4]);
50402         this.mainHd = new E(this.mainWrap.dom.firstChild);
50403         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
50404
50405         this.footerPanel = new E(cs[5]);
50406         this.footerPanel.enableDisplayMode("block");
50407
50408         this.resizeProxy = new E(cs[6]);
50409
50410         this.headerSelector = String.format(
50411            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
50412            this.lockedHd.id, this.mainHd.id
50413         );
50414
50415         this.splitterSelector = String.format(
50416            '#{0} div.x-grid-split, #{1} div.x-grid-split',
50417            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
50418         );
50419     },
50420     idToCssName : function(s)
50421     {
50422         return s.replace(/[^a-z0-9]+/ig, '-');
50423     },
50424
50425     getHeaderCell : function(index){
50426         return Roo.DomQuery.select(this.headerSelector)[index];
50427     },
50428
50429     getHeaderCellMeasure : function(index){
50430         return this.getHeaderCell(index).firstChild;
50431     },
50432
50433     getHeaderCellText : function(index){
50434         return this.getHeaderCell(index).firstChild.firstChild;
50435     },
50436
50437     getLockedTable : function(){
50438         return this.lockedBody.dom.firstChild;
50439     },
50440
50441     getBodyTable : function(){
50442         return this.mainBody.dom.firstChild;
50443     },
50444
50445     getLockedRow : function(index){
50446         return this.getLockedTable().rows[index];
50447     },
50448
50449     getRow : function(index){
50450         return this.getBodyTable().rows[index];
50451     },
50452
50453     getRowComposite : function(index){
50454         if(!this.rowEl){
50455             this.rowEl = new Roo.CompositeElementLite();
50456         }
50457         var els = [], lrow, mrow;
50458         if(lrow = this.getLockedRow(index)){
50459             els.push(lrow);
50460         }
50461         if(mrow = this.getRow(index)){
50462             els.push(mrow);
50463         }
50464         this.rowEl.elements = els;
50465         return this.rowEl;
50466     },
50467     /**
50468      * Gets the 'td' of the cell
50469      * 
50470      * @param {Integer} rowIndex row to select
50471      * @param {Integer} colIndex column to select
50472      * 
50473      * @return {Object} 
50474      */
50475     getCell : function(rowIndex, colIndex){
50476         var locked = this.cm.getLockedCount();
50477         var source;
50478         if(colIndex < locked){
50479             source = this.lockedBody.dom.firstChild;
50480         }else{
50481             source = this.mainBody.dom.firstChild;
50482             colIndex -= locked;
50483         }
50484         return source.rows[rowIndex].childNodes[colIndex];
50485     },
50486
50487     getCellText : function(rowIndex, colIndex){
50488         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
50489     },
50490
50491     getCellBox : function(cell){
50492         var b = this.fly(cell).getBox();
50493         if(Roo.isOpera){ // opera fails to report the Y
50494             b.y = cell.offsetTop + this.mainBody.getY();
50495         }
50496         return b;
50497     },
50498
50499     getCellIndex : function(cell){
50500         var id = String(cell.className).match(this.cellRE);
50501         if(id){
50502             return parseInt(id[1], 10);
50503         }
50504         return 0;
50505     },
50506
50507     findHeaderIndex : function(n){
50508         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
50509         return r ? this.getCellIndex(r) : false;
50510     },
50511
50512     findHeaderCell : function(n){
50513         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
50514         return r ? r : false;
50515     },
50516
50517     findRowIndex : function(n){
50518         if(!n){
50519             return false;
50520         }
50521         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
50522         return r ? r.rowIndex : false;
50523     },
50524
50525     findCellIndex : function(node){
50526         var stop = this.el.dom;
50527         while(node && node != stop){
50528             if(this.findRE.test(node.className)){
50529                 return this.getCellIndex(node);
50530             }
50531             node = node.parentNode;
50532         }
50533         return false;
50534     },
50535
50536     getColumnId : function(index){
50537         return this.cm.getColumnId(index);
50538     },
50539
50540     getSplitters : function()
50541     {
50542         if(this.splitterSelector){
50543            return Roo.DomQuery.select(this.splitterSelector);
50544         }else{
50545             return null;
50546       }
50547     },
50548
50549     getSplitter : function(index){
50550         return this.getSplitters()[index];
50551     },
50552
50553     onRowOver : function(e, t){
50554         var row;
50555         if((row = this.findRowIndex(t)) !== false){
50556             this.getRowComposite(row).addClass("x-grid-row-over");
50557         }
50558     },
50559
50560     onRowOut : function(e, t){
50561         var row;
50562         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
50563             this.getRowComposite(row).removeClass("x-grid-row-over");
50564         }
50565     },
50566
50567     renderHeaders : function(){
50568         var cm = this.cm;
50569         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
50570         var cb = [], lb = [], sb = [], lsb = [], p = {};
50571         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50572             p.cellId = "x-grid-hd-0-" + i;
50573             p.splitId = "x-grid-csplit-0-" + i;
50574             p.id = cm.getColumnId(i);
50575             p.title = cm.getColumnTooltip(i) || "";
50576             p.value = cm.getColumnHeader(i) || "";
50577             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
50578             if(!cm.isLocked(i)){
50579                 cb[cb.length] = ct.apply(p);
50580                 sb[sb.length] = st.apply(p);
50581             }else{
50582                 lb[lb.length] = ct.apply(p);
50583                 lsb[lsb.length] = st.apply(p);
50584             }
50585         }
50586         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
50587                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
50588     },
50589
50590     updateHeaders : function(){
50591         var html = this.renderHeaders();
50592         this.lockedHd.update(html[0]);
50593         this.mainHd.update(html[1]);
50594     },
50595
50596     /**
50597      * Focuses the specified row.
50598      * @param {Number} row The row index
50599      */
50600     focusRow : function(row)
50601     {
50602         //Roo.log('GridView.focusRow');
50603         var x = this.scroller.dom.scrollLeft;
50604         this.focusCell(row, 0, false);
50605         this.scroller.dom.scrollLeft = x;
50606     },
50607
50608     /**
50609      * Focuses the specified cell.
50610      * @param {Number} row The row index
50611      * @param {Number} col The column index
50612      * @param {Boolean} hscroll false to disable horizontal scrolling
50613      */
50614     focusCell : function(row, col, hscroll)
50615     {
50616         //Roo.log('GridView.focusCell');
50617         var el = this.ensureVisible(row, col, hscroll);
50618         this.focusEl.alignTo(el, "tl-tl");
50619         if(Roo.isGecko){
50620             this.focusEl.focus();
50621         }else{
50622             this.focusEl.focus.defer(1, this.focusEl);
50623         }
50624     },
50625
50626     /**
50627      * Scrolls the specified cell into view
50628      * @param {Number} row The row index
50629      * @param {Number} col The column index
50630      * @param {Boolean} hscroll false to disable horizontal scrolling
50631      */
50632     ensureVisible : function(row, col, hscroll)
50633     {
50634         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
50635         //return null; //disable for testing.
50636         if(typeof row != "number"){
50637             row = row.rowIndex;
50638         }
50639         if(row < 0 && row >= this.ds.getCount()){
50640             return  null;
50641         }
50642         col = (col !== undefined ? col : 0);
50643         var cm = this.grid.colModel;
50644         while(cm.isHidden(col)){
50645             col++;
50646         }
50647
50648         var el = this.getCell(row, col);
50649         if(!el){
50650             return null;
50651         }
50652         var c = this.scroller.dom;
50653
50654         var ctop = parseInt(el.offsetTop, 10);
50655         var cleft = parseInt(el.offsetLeft, 10);
50656         var cbot = ctop + el.offsetHeight;
50657         var cright = cleft + el.offsetWidth;
50658         
50659         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
50660         var stop = parseInt(c.scrollTop, 10);
50661         var sleft = parseInt(c.scrollLeft, 10);
50662         var sbot = stop + ch;
50663         var sright = sleft + c.clientWidth;
50664         /*
50665         Roo.log('GridView.ensureVisible:' +
50666                 ' ctop:' + ctop +
50667                 ' c.clientHeight:' + c.clientHeight +
50668                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
50669                 ' stop:' + stop +
50670                 ' cbot:' + cbot +
50671                 ' sbot:' + sbot +
50672                 ' ch:' + ch  
50673                 );
50674         */
50675         if(ctop < stop){
50676              c.scrollTop = ctop;
50677             //Roo.log("set scrolltop to ctop DISABLE?");
50678         }else if(cbot > sbot){
50679             //Roo.log("set scrolltop to cbot-ch");
50680             c.scrollTop = cbot-ch;
50681         }
50682         
50683         if(hscroll !== false){
50684             if(cleft < sleft){
50685                 c.scrollLeft = cleft;
50686             }else if(cright > sright){
50687                 c.scrollLeft = cright-c.clientWidth;
50688             }
50689         }
50690          
50691         return el;
50692     },
50693
50694     updateColumns : function(){
50695         this.grid.stopEditing();
50696         var cm = this.grid.colModel, colIds = this.getColumnIds();
50697         //var totalWidth = cm.getTotalWidth();
50698         var pos = 0;
50699         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50700             //if(cm.isHidden(i)) continue;
50701             var w = cm.getColumnWidth(i);
50702             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
50703             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
50704         }
50705         this.updateSplitters();
50706     },
50707
50708     generateRules : function(cm){
50709         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
50710         Roo.util.CSS.removeStyleSheet(rulesId);
50711         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50712             var cid = cm.getColumnId(i);
50713             var align = '';
50714             if(cm.config[i].align){
50715                 align = 'text-align:'+cm.config[i].align+';';
50716             }
50717             var hidden = '';
50718             if(cm.isHidden(i)){
50719                 hidden = 'display:none;';
50720             }
50721             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
50722             ruleBuf.push(
50723                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
50724                     this.hdSelector, cid, " {\n", align, width, "}\n",
50725                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
50726                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
50727         }
50728         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
50729     },
50730
50731     updateSplitters : function(){
50732         var cm = this.cm, s = this.getSplitters();
50733         if(s){ // splitters not created yet
50734             var pos = 0, locked = true;
50735             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50736                 if(cm.isHidden(i)) continue;
50737                 var w = cm.getColumnWidth(i); // make sure it's a number
50738                 if(!cm.isLocked(i) && locked){
50739                     pos = 0;
50740                     locked = false;
50741                 }
50742                 pos += w;
50743                 s[i].style.left = (pos-this.splitOffset) + "px";
50744             }
50745         }
50746     },
50747
50748     handleHiddenChange : function(colModel, colIndex, hidden){
50749         if(hidden){
50750             this.hideColumn(colIndex);
50751         }else{
50752             this.unhideColumn(colIndex);
50753         }
50754     },
50755
50756     hideColumn : function(colIndex){
50757         var cid = this.getColumnId(colIndex);
50758         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
50759         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
50760         if(Roo.isSafari){
50761             this.updateHeaders();
50762         }
50763         this.updateSplitters();
50764         this.layout();
50765     },
50766
50767     unhideColumn : function(colIndex){
50768         var cid = this.getColumnId(colIndex);
50769         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
50770         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
50771
50772         if(Roo.isSafari){
50773             this.updateHeaders();
50774         }
50775         this.updateSplitters();
50776         this.layout();
50777     },
50778
50779     insertRows : function(dm, firstRow, lastRow, isUpdate){
50780         if(firstRow == 0 && lastRow == dm.getCount()-1){
50781             this.refresh();
50782         }else{
50783             if(!isUpdate){
50784                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
50785             }
50786             var s = this.getScrollState();
50787             var markup = this.renderRows(firstRow, lastRow);
50788             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
50789             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
50790             this.restoreScroll(s);
50791             if(!isUpdate){
50792                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
50793                 this.syncRowHeights(firstRow, lastRow);
50794                 this.stripeRows(firstRow);
50795                 this.layout();
50796             }
50797         }
50798     },
50799
50800     bufferRows : function(markup, target, index){
50801         var before = null, trows = target.rows, tbody = target.tBodies[0];
50802         if(index < trows.length){
50803             before = trows[index];
50804         }
50805         var b = document.createElement("div");
50806         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
50807         var rows = b.firstChild.rows;
50808         for(var i = 0, len = rows.length; i < len; i++){
50809             if(before){
50810                 tbody.insertBefore(rows[0], before);
50811             }else{
50812                 tbody.appendChild(rows[0]);
50813             }
50814         }
50815         b.innerHTML = "";
50816         b = null;
50817     },
50818
50819     deleteRows : function(dm, firstRow, lastRow){
50820         if(dm.getRowCount()<1){
50821             this.fireEvent("beforerefresh", this);
50822             this.mainBody.update("");
50823             this.lockedBody.update("");
50824             this.fireEvent("refresh", this);
50825         }else{
50826             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
50827             var bt = this.getBodyTable();
50828             var tbody = bt.firstChild;
50829             var rows = bt.rows;
50830             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
50831                 tbody.removeChild(rows[firstRow]);
50832             }
50833             this.stripeRows(firstRow);
50834             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
50835         }
50836     },
50837
50838     updateRows : function(dataSource, firstRow, lastRow){
50839         var s = this.getScrollState();
50840         this.refresh();
50841         this.restoreScroll(s);
50842     },
50843
50844     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
50845         if(!noRefresh){
50846            this.refresh();
50847         }
50848         this.updateHeaderSortState();
50849     },
50850
50851     getScrollState : function(){
50852         
50853         var sb = this.scroller.dom;
50854         return {left: sb.scrollLeft, top: sb.scrollTop};
50855     },
50856
50857     stripeRows : function(startRow){
50858         if(!this.grid.stripeRows || this.ds.getCount() < 1){
50859             return;
50860         }
50861         startRow = startRow || 0;
50862         var rows = this.getBodyTable().rows;
50863         var lrows = this.getLockedTable().rows;
50864         var cls = ' x-grid-row-alt ';
50865         for(var i = startRow, len = rows.length; i < len; i++){
50866             var row = rows[i], lrow = lrows[i];
50867             var isAlt = ((i+1) % 2 == 0);
50868             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
50869             if(isAlt == hasAlt){
50870                 continue;
50871             }
50872             if(isAlt){
50873                 row.className += " x-grid-row-alt";
50874             }else{
50875                 row.className = row.className.replace("x-grid-row-alt", "");
50876             }
50877             if(lrow){
50878                 lrow.className = row.className;
50879             }
50880         }
50881     },
50882
50883     restoreScroll : function(state){
50884         //Roo.log('GridView.restoreScroll');
50885         var sb = this.scroller.dom;
50886         sb.scrollLeft = state.left;
50887         sb.scrollTop = state.top;
50888         this.syncScroll();
50889     },
50890
50891     syncScroll : function(){
50892         //Roo.log('GridView.syncScroll');
50893         var sb = this.scroller.dom;
50894         var sh = this.mainHd.dom;
50895         var bs = this.mainBody.dom;
50896         var lv = this.lockedBody.dom;
50897         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
50898         lv.scrollTop = bs.scrollTop = sb.scrollTop;
50899     },
50900
50901     handleScroll : function(e){
50902         this.syncScroll();
50903         var sb = this.scroller.dom;
50904         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
50905         e.stopEvent();
50906     },
50907
50908     handleWheel : function(e){
50909         var d = e.getWheelDelta();
50910         this.scroller.dom.scrollTop -= d*22;
50911         // set this here to prevent jumpy scrolling on large tables
50912         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
50913         e.stopEvent();
50914     },
50915
50916     renderRows : function(startRow, endRow){
50917         // pull in all the crap needed to render rows
50918         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
50919         var colCount = cm.getColumnCount();
50920
50921         if(ds.getCount() < 1){
50922             return ["", ""];
50923         }
50924
50925         // build a map for all the columns
50926         var cs = [];
50927         for(var i = 0; i < colCount; i++){
50928             var name = cm.getDataIndex(i);
50929             cs[i] = {
50930                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
50931                 renderer : cm.getRenderer(i),
50932                 id : cm.getColumnId(i),
50933                 locked : cm.isLocked(i)
50934             };
50935         }
50936
50937         startRow = startRow || 0;
50938         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
50939
50940         // records to render
50941         var rs = ds.getRange(startRow, endRow);
50942
50943         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
50944     },
50945
50946     // As much as I hate to duplicate code, this was branched because FireFox really hates
50947     // [].join("") on strings. The performance difference was substantial enough to
50948     // branch this function
50949     doRender : Roo.isGecko ?
50950             function(cs, rs, ds, startRow, colCount, stripe){
50951                 var ts = this.templates, ct = ts.cell, rt = ts.row;
50952                 // buffers
50953                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
50954                 
50955                 var hasListener = this.grid.hasListener('rowclass');
50956                 var rowcfg = {};
50957                 for(var j = 0, len = rs.length; j < len; j++){
50958                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
50959                     for(var i = 0; i < colCount; i++){
50960                         c = cs[i];
50961                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
50962                         p.id = c.id;
50963                         p.css = p.attr = "";
50964                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
50965                         if(p.value == undefined || p.value === "") p.value = "&#160;";
50966                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
50967                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
50968                         }
50969                         var markup = ct.apply(p);
50970                         if(!c.locked){
50971                             cb+= markup;
50972                         }else{
50973                             lcb+= markup;
50974                         }
50975                     }
50976                     var alt = [];
50977                     if(stripe && ((rowIndex+1) % 2 == 0)){
50978                         alt.push("x-grid-row-alt")
50979                     }
50980                     if(r.dirty){
50981                         alt.push(  " x-grid-dirty-row");
50982                     }
50983                     rp.cells = lcb;
50984                     if(this.getRowClass){
50985                         alt.push(this.getRowClass(r, rowIndex));
50986                     }
50987                     if (hasListener) {
50988                         rowcfg = {
50989                              
50990                             record: r,
50991                             rowIndex : rowIndex,
50992                             rowClass : ''
50993                         }
50994                         this.grid.fireEvent('rowclass', this, rowcfg);
50995                         alt.push(rowcfg.rowClass);
50996                     }
50997                     rp.alt = alt.join(" ");
50998                     lbuf+= rt.apply(rp);
50999                     rp.cells = cb;
51000                     buf+=  rt.apply(rp);
51001                 }
51002                 return [lbuf, buf];
51003             } :
51004             function(cs, rs, ds, startRow, colCount, stripe){
51005                 var ts = this.templates, ct = ts.cell, rt = ts.row;
51006                 // buffers
51007                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
51008                 var hasListener = this.grid.hasListener('rowclass');
51009  
51010                 var rowcfg = {};
51011                 for(var j = 0, len = rs.length; j < len; j++){
51012                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
51013                     for(var i = 0; i < colCount; i++){
51014                         c = cs[i];
51015                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
51016                         p.id = c.id;
51017                         p.css = p.attr = "";
51018                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
51019                         if(p.value == undefined || p.value === "") p.value = "&#160;";
51020                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
51021                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
51022                         }
51023                         
51024                         var markup = ct.apply(p);
51025                         if(!c.locked){
51026                             cb[cb.length] = markup;
51027                         }else{
51028                             lcb[lcb.length] = markup;
51029                         }
51030                     }
51031                     var alt = [];
51032                     if(stripe && ((rowIndex+1) % 2 == 0)){
51033                         alt.push( "x-grid-row-alt");
51034                     }
51035                     if(r.dirty){
51036                         alt.push(" x-grid-dirty-row");
51037                     }
51038                     rp.cells = lcb;
51039                     if(this.getRowClass){
51040                         alt.push( this.getRowClass(r, rowIndex));
51041                     }
51042                     if (hasListener) {
51043                         rowcfg = {
51044                              
51045                             record: r,
51046                             rowIndex : rowIndex,
51047                             rowClass : ''
51048                         }
51049                         this.grid.fireEvent('rowclass', this, rowcfg);
51050                         alt.push(rowcfg.rowClass);
51051                     }
51052                     rp.alt = alt.join(" ");
51053                     rp.cells = lcb.join("");
51054                     lbuf[lbuf.length] = rt.apply(rp);
51055                     rp.cells = cb.join("");
51056                     buf[buf.length] =  rt.apply(rp);
51057                 }
51058                 return [lbuf.join(""), buf.join("")];
51059             },
51060
51061     renderBody : function(){
51062         var markup = this.renderRows();
51063         var bt = this.templates.body;
51064         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
51065     },
51066
51067     /**
51068      * Refreshes the grid
51069      * @param {Boolean} headersToo
51070      */
51071     refresh : function(headersToo){
51072         this.fireEvent("beforerefresh", this);
51073         this.grid.stopEditing();
51074         var result = this.renderBody();
51075         this.lockedBody.update(result[0]);
51076         this.mainBody.update(result[1]);
51077         if(headersToo === true){
51078             this.updateHeaders();
51079             this.updateColumns();
51080             this.updateSplitters();
51081             this.updateHeaderSortState();
51082         }
51083         this.syncRowHeights();
51084         this.layout();
51085         this.fireEvent("refresh", this);
51086     },
51087
51088     handleColumnMove : function(cm, oldIndex, newIndex){
51089         this.indexMap = null;
51090         var s = this.getScrollState();
51091         this.refresh(true);
51092         this.restoreScroll(s);
51093         this.afterMove(newIndex);
51094     },
51095
51096     afterMove : function(colIndex){
51097         if(this.enableMoveAnim && Roo.enableFx){
51098             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
51099         }
51100         // if multisort - fix sortOrder, and reload..
51101         if (this.grid.dataSource.multiSort) {
51102             // the we can call sort again..
51103             var dm = this.grid.dataSource;
51104             var cm = this.grid.colModel;
51105             var so = [];
51106             for(var i = 0; i < cm.config.length; i++ ) {
51107                 
51108                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
51109                     continue; // dont' bother, it's not in sort list or being set.
51110                 }
51111                 
51112                 so.push(cm.config[i].dataIndex);
51113             };
51114             dm.sortOrder = so;
51115             dm.load(dm.lastOptions);
51116             
51117             
51118         }
51119         
51120     },
51121
51122     updateCell : function(dm, rowIndex, dataIndex){
51123         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
51124         if(typeof colIndex == "undefined"){ // not present in grid
51125             return;
51126         }
51127         var cm = this.grid.colModel;
51128         var cell = this.getCell(rowIndex, colIndex);
51129         var cellText = this.getCellText(rowIndex, colIndex);
51130
51131         var p = {
51132             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
51133             id : cm.getColumnId(colIndex),
51134             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
51135         };
51136         var renderer = cm.getRenderer(colIndex);
51137         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
51138         if(typeof val == "undefined" || val === "") val = "&#160;";
51139         cellText.innerHTML = val;
51140         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
51141         this.syncRowHeights(rowIndex, rowIndex);
51142     },
51143
51144     calcColumnWidth : function(colIndex, maxRowsToMeasure){
51145         var maxWidth = 0;
51146         if(this.grid.autoSizeHeaders){
51147             var h = this.getHeaderCellMeasure(colIndex);
51148             maxWidth = Math.max(maxWidth, h.scrollWidth);
51149         }
51150         var tb, index;
51151         if(this.cm.isLocked(colIndex)){
51152             tb = this.getLockedTable();
51153             index = colIndex;
51154         }else{
51155             tb = this.getBodyTable();
51156             index = colIndex - this.cm.getLockedCount();
51157         }
51158         if(tb && tb.rows){
51159             var rows = tb.rows;
51160             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
51161             for(var i = 0; i < stopIndex; i++){
51162                 var cell = rows[i].childNodes[index].firstChild;
51163                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
51164             }
51165         }
51166         return maxWidth + /*margin for error in IE*/ 5;
51167     },
51168     /**
51169      * Autofit a column to its content.
51170      * @param {Number} colIndex
51171      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
51172      */
51173      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
51174          if(this.cm.isHidden(colIndex)){
51175              return; // can't calc a hidden column
51176          }
51177         if(forceMinSize){
51178             var cid = this.cm.getColumnId(colIndex);
51179             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
51180            if(this.grid.autoSizeHeaders){
51181                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
51182            }
51183         }
51184         var newWidth = this.calcColumnWidth(colIndex);
51185         this.cm.setColumnWidth(colIndex,
51186             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
51187         if(!suppressEvent){
51188             this.grid.fireEvent("columnresize", colIndex, newWidth);
51189         }
51190     },
51191
51192     /**
51193      * Autofits all columns to their content and then expands to fit any extra space in the grid
51194      */
51195      autoSizeColumns : function(){
51196         var cm = this.grid.colModel;
51197         var colCount = cm.getColumnCount();
51198         for(var i = 0; i < colCount; i++){
51199             this.autoSizeColumn(i, true, true);
51200         }
51201         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
51202             this.fitColumns();
51203         }else{
51204             this.updateColumns();
51205             this.layout();
51206         }
51207     },
51208
51209     /**
51210      * Autofits all columns to the grid's width proportionate with their current size
51211      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
51212      */
51213     fitColumns : function(reserveScrollSpace){
51214         var cm = this.grid.colModel;
51215         var colCount = cm.getColumnCount();
51216         var cols = [];
51217         var width = 0;
51218         var i, w;
51219         for (i = 0; i < colCount; i++){
51220             if(!cm.isHidden(i) && !cm.isFixed(i)){
51221                 w = cm.getColumnWidth(i);
51222                 cols.push(i);
51223                 cols.push(w);
51224                 width += w;
51225             }
51226         }
51227         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
51228         if(reserveScrollSpace){
51229             avail -= 17;
51230         }
51231         var frac = (avail - cm.getTotalWidth())/width;
51232         while (cols.length){
51233             w = cols.pop();
51234             i = cols.pop();
51235             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
51236         }
51237         this.updateColumns();
51238         this.layout();
51239     },
51240
51241     onRowSelect : function(rowIndex){
51242         var row = this.getRowComposite(rowIndex);
51243         row.addClass("x-grid-row-selected");
51244     },
51245
51246     onRowDeselect : function(rowIndex){
51247         var row = this.getRowComposite(rowIndex);
51248         row.removeClass("x-grid-row-selected");
51249     },
51250
51251     onCellSelect : function(row, col){
51252         var cell = this.getCell(row, col);
51253         if(cell){
51254             Roo.fly(cell).addClass("x-grid-cell-selected");
51255         }
51256     },
51257
51258     onCellDeselect : function(row, col){
51259         var cell = this.getCell(row, col);
51260         if(cell){
51261             Roo.fly(cell).removeClass("x-grid-cell-selected");
51262         }
51263     },
51264
51265     updateHeaderSortState : function(){
51266         
51267         // sort state can be single { field: xxx, direction : yyy}
51268         // or   { xxx=>ASC , yyy : DESC ..... }
51269         
51270         var mstate = {};
51271         if (!this.ds.multiSort) { 
51272             var state = this.ds.getSortState();
51273             if(!state){
51274                 return;
51275             }
51276             mstate[state.field] = state.direction;
51277             // FIXME... - this is not used here.. but might be elsewhere..
51278             this.sortState = state;
51279             
51280         } else {
51281             mstate = this.ds.sortToggle;
51282         }
51283         //remove existing sort classes..
51284         
51285         var sc = this.sortClasses;
51286         var hds = this.el.select(this.headerSelector).removeClass(sc);
51287         
51288         for(var f in mstate) {
51289         
51290             var sortColumn = this.cm.findColumnIndex(f);
51291             
51292             if(sortColumn != -1){
51293                 var sortDir = mstate[f];        
51294                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
51295             }
51296         }
51297         
51298          
51299         
51300     },
51301
51302
51303     handleHeaderClick : function(g, index){
51304         if(this.headersDisabled){
51305             return;
51306         }
51307         var dm = g.dataSource, cm = g.colModel;
51308         if(!cm.isSortable(index)){
51309             return;
51310         }
51311         g.stopEditing();
51312         
51313         if (dm.multiSort) {
51314             // update the sortOrder
51315             var so = [];
51316             for(var i = 0; i < cm.config.length; i++ ) {
51317                 
51318                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
51319                     continue; // dont' bother, it's not in sort list or being set.
51320                 }
51321                 
51322                 so.push(cm.config[i].dataIndex);
51323             };
51324             dm.sortOrder = so;
51325         }
51326         
51327         
51328         dm.sort(cm.getDataIndex(index));
51329     },
51330
51331
51332     destroy : function(){
51333         if(this.colMenu){
51334             this.colMenu.removeAll();
51335             Roo.menu.MenuMgr.unregister(this.colMenu);
51336             this.colMenu.getEl().remove();
51337             delete this.colMenu;
51338         }
51339         if(this.hmenu){
51340             this.hmenu.removeAll();
51341             Roo.menu.MenuMgr.unregister(this.hmenu);
51342             this.hmenu.getEl().remove();
51343             delete this.hmenu;
51344         }
51345         if(this.grid.enableColumnMove){
51346             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
51347             if(dds){
51348                 for(var dd in dds){
51349                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
51350                         var elid = dds[dd].dragElId;
51351                         dds[dd].unreg();
51352                         Roo.get(elid).remove();
51353                     } else if(dds[dd].config.isTarget){
51354                         dds[dd].proxyTop.remove();
51355                         dds[dd].proxyBottom.remove();
51356                         dds[dd].unreg();
51357                     }
51358                     if(Roo.dd.DDM.locationCache[dd]){
51359                         delete Roo.dd.DDM.locationCache[dd];
51360                     }
51361                 }
51362                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
51363             }
51364         }
51365         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
51366         this.bind(null, null);
51367         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
51368     },
51369
51370     handleLockChange : function(){
51371         this.refresh(true);
51372     },
51373
51374     onDenyColumnLock : function(){
51375
51376     },
51377
51378     onDenyColumnHide : function(){
51379
51380     },
51381
51382     handleHdMenuClick : function(item){
51383         var index = this.hdCtxIndex;
51384         var cm = this.cm, ds = this.ds;
51385         switch(item.id){
51386             case "asc":
51387                 ds.sort(cm.getDataIndex(index), "ASC");
51388                 break;
51389             case "desc":
51390                 ds.sort(cm.getDataIndex(index), "DESC");
51391                 break;
51392             case "lock":
51393                 var lc = cm.getLockedCount();
51394                 if(cm.getColumnCount(true) <= lc+1){
51395                     this.onDenyColumnLock();
51396                     return;
51397                 }
51398                 if(lc != index){
51399                     cm.setLocked(index, true, true);
51400                     cm.moveColumn(index, lc);
51401                     this.grid.fireEvent("columnmove", index, lc);
51402                 }else{
51403                     cm.setLocked(index, true);
51404                 }
51405             break;
51406             case "unlock":
51407                 var lc = cm.getLockedCount();
51408                 if((lc-1) != index){
51409                     cm.setLocked(index, false, true);
51410                     cm.moveColumn(index, lc-1);
51411                     this.grid.fireEvent("columnmove", index, lc-1);
51412                 }else{
51413                     cm.setLocked(index, false);
51414                 }
51415             break;
51416             default:
51417                 index = cm.getIndexById(item.id.substr(4));
51418                 if(index != -1){
51419                     if(item.checked && cm.getColumnCount(true) <= 1){
51420                         this.onDenyColumnHide();
51421                         return false;
51422                     }
51423                     cm.setHidden(index, item.checked);
51424                 }
51425         }
51426         return true;
51427     },
51428
51429     beforeColMenuShow : function(){
51430         var cm = this.cm,  colCount = cm.getColumnCount();
51431         this.colMenu.removeAll();
51432         for(var i = 0; i < colCount; i++){
51433             this.colMenu.add(new Roo.menu.CheckItem({
51434                 id: "col-"+cm.getColumnId(i),
51435                 text: cm.getColumnHeader(i),
51436                 checked: !cm.isHidden(i),
51437                 hideOnClick:false
51438             }));
51439         }
51440     },
51441
51442     handleHdCtx : function(g, index, e){
51443         e.stopEvent();
51444         var hd = this.getHeaderCell(index);
51445         this.hdCtxIndex = index;
51446         var ms = this.hmenu.items, cm = this.cm;
51447         ms.get("asc").setDisabled(!cm.isSortable(index));
51448         ms.get("desc").setDisabled(!cm.isSortable(index));
51449         if(this.grid.enableColLock !== false){
51450             ms.get("lock").setDisabled(cm.isLocked(index));
51451             ms.get("unlock").setDisabled(!cm.isLocked(index));
51452         }
51453         this.hmenu.show(hd, "tl-bl");
51454     },
51455
51456     handleHdOver : function(e){
51457         var hd = this.findHeaderCell(e.getTarget());
51458         if(hd && !this.headersDisabled){
51459             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
51460                this.fly(hd).addClass("x-grid-hd-over");
51461             }
51462         }
51463     },
51464
51465     handleHdOut : function(e){
51466         var hd = this.findHeaderCell(e.getTarget());
51467         if(hd){
51468             this.fly(hd).removeClass("x-grid-hd-over");
51469         }
51470     },
51471
51472     handleSplitDblClick : function(e, t){
51473         var i = this.getCellIndex(t);
51474         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
51475             this.autoSizeColumn(i, true);
51476             this.layout();
51477         }
51478     },
51479
51480     render : function(){
51481
51482         var cm = this.cm;
51483         var colCount = cm.getColumnCount();
51484
51485         if(this.grid.monitorWindowResize === true){
51486             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51487         }
51488         var header = this.renderHeaders();
51489         var body = this.templates.body.apply({rows:""});
51490         var html = this.templates.master.apply({
51491             lockedBody: body,
51492             body: body,
51493             lockedHeader: header[0],
51494             header: header[1]
51495         });
51496
51497         //this.updateColumns();
51498
51499         this.grid.getGridEl().dom.innerHTML = html;
51500
51501         this.initElements();
51502         
51503         // a kludge to fix the random scolling effect in webkit
51504         this.el.on("scroll", function() {
51505             this.el.dom.scrollTop=0; // hopefully not recursive..
51506         },this);
51507
51508         this.scroller.on("scroll", this.handleScroll, this);
51509         this.lockedBody.on("mousewheel", this.handleWheel, this);
51510         this.mainBody.on("mousewheel", this.handleWheel, this);
51511
51512         this.mainHd.on("mouseover", this.handleHdOver, this);
51513         this.mainHd.on("mouseout", this.handleHdOut, this);
51514         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
51515                 {delegate: "."+this.splitClass});
51516
51517         this.lockedHd.on("mouseover", this.handleHdOver, this);
51518         this.lockedHd.on("mouseout", this.handleHdOut, this);
51519         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
51520                 {delegate: "."+this.splitClass});
51521
51522         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
51523             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
51524         }
51525
51526         this.updateSplitters();
51527
51528         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
51529             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
51530             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
51531         }
51532
51533         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
51534             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
51535             this.hmenu.add(
51536                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
51537                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
51538             );
51539             if(this.grid.enableColLock !== false){
51540                 this.hmenu.add('-',
51541                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
51542                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
51543                 );
51544             }
51545             if(this.grid.enableColumnHide !== false){
51546
51547                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
51548                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
51549                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
51550
51551                 this.hmenu.add('-',
51552                     {id:"columns", text: this.columnsText, menu: this.colMenu}
51553                 );
51554             }
51555             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
51556
51557             this.grid.on("headercontextmenu", this.handleHdCtx, this);
51558         }
51559
51560         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
51561             this.dd = new Roo.grid.GridDragZone(this.grid, {
51562                 ddGroup : this.grid.ddGroup || 'GridDD'
51563             });
51564             
51565         }
51566
51567         /*
51568         for(var i = 0; i < colCount; i++){
51569             if(cm.isHidden(i)){
51570                 this.hideColumn(i);
51571             }
51572             if(cm.config[i].align){
51573                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
51574                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
51575             }
51576         }*/
51577         
51578         this.updateHeaderSortState();
51579
51580         this.beforeInitialResize();
51581         this.layout(true);
51582
51583         // two part rendering gives faster view to the user
51584         this.renderPhase2.defer(1, this);
51585     },
51586
51587     renderPhase2 : function(){
51588         // render the rows now
51589         this.refresh();
51590         if(this.grid.autoSizeColumns){
51591             this.autoSizeColumns();
51592         }
51593     },
51594
51595     beforeInitialResize : function(){
51596
51597     },
51598
51599     onColumnSplitterMoved : function(i, w){
51600         this.userResized = true;
51601         var cm = this.grid.colModel;
51602         cm.setColumnWidth(i, w, true);
51603         var cid = cm.getColumnId(i);
51604         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
51605         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
51606         this.updateSplitters();
51607         this.layout();
51608         this.grid.fireEvent("columnresize", i, w);
51609     },
51610
51611     syncRowHeights : function(startIndex, endIndex){
51612         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
51613             startIndex = startIndex || 0;
51614             var mrows = this.getBodyTable().rows;
51615             var lrows = this.getLockedTable().rows;
51616             var len = mrows.length-1;
51617             endIndex = Math.min(endIndex || len, len);
51618             for(var i = startIndex; i <= endIndex; i++){
51619                 var m = mrows[i], l = lrows[i];
51620                 var h = Math.max(m.offsetHeight, l.offsetHeight);
51621                 m.style.height = l.style.height = h + "px";
51622             }
51623         }
51624     },
51625
51626     layout : function(initialRender, is2ndPass){
51627         var g = this.grid;
51628         var auto = g.autoHeight;
51629         var scrollOffset = 16;
51630         var c = g.getGridEl(), cm = this.cm,
51631                 expandCol = g.autoExpandColumn,
51632                 gv = this;
51633         //c.beginMeasure();
51634
51635         if(!c.dom.offsetWidth){ // display:none?
51636             if(initialRender){
51637                 this.lockedWrap.show();
51638                 this.mainWrap.show();
51639             }
51640             return;
51641         }
51642
51643         var hasLock = this.cm.isLocked(0);
51644
51645         var tbh = this.headerPanel.getHeight();
51646         var bbh = this.footerPanel.getHeight();
51647
51648         if(auto){
51649             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
51650             var newHeight = ch + c.getBorderWidth("tb");
51651             if(g.maxHeight){
51652                 newHeight = Math.min(g.maxHeight, newHeight);
51653             }
51654             c.setHeight(newHeight);
51655         }
51656
51657         if(g.autoWidth){
51658             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
51659         }
51660
51661         var s = this.scroller;
51662
51663         var csize = c.getSize(true);
51664
51665         this.el.setSize(csize.width, csize.height);
51666
51667         this.headerPanel.setWidth(csize.width);
51668         this.footerPanel.setWidth(csize.width);
51669
51670         var hdHeight = this.mainHd.getHeight();
51671         var vw = csize.width;
51672         var vh = csize.height - (tbh + bbh);
51673
51674         s.setSize(vw, vh);
51675
51676         var bt = this.getBodyTable();
51677         var ltWidth = hasLock ?
51678                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
51679
51680         var scrollHeight = bt.offsetHeight;
51681         var scrollWidth = ltWidth + bt.offsetWidth;
51682         var vscroll = false, hscroll = false;
51683
51684         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
51685
51686         var lw = this.lockedWrap, mw = this.mainWrap;
51687         var lb = this.lockedBody, mb = this.mainBody;
51688
51689         setTimeout(function(){
51690             var t = s.dom.offsetTop;
51691             var w = s.dom.clientWidth,
51692                 h = s.dom.clientHeight;
51693
51694             lw.setTop(t);
51695             lw.setSize(ltWidth, h);
51696
51697             mw.setLeftTop(ltWidth, t);
51698             mw.setSize(w-ltWidth, h);
51699
51700             lb.setHeight(h-hdHeight);
51701             mb.setHeight(h-hdHeight);
51702
51703             if(is2ndPass !== true && !gv.userResized && expandCol){
51704                 // high speed resize without full column calculation
51705                 
51706                 var ci = cm.getIndexById(expandCol);
51707                 if (ci < 0) {
51708                     ci = cm.findColumnIndex(expandCol);
51709                 }
51710                 ci = Math.max(0, ci); // make sure it's got at least the first col.
51711                 var expandId = cm.getColumnId(ci);
51712                 var  tw = cm.getTotalWidth(false);
51713                 var currentWidth = cm.getColumnWidth(ci);
51714                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
51715                 if(currentWidth != cw){
51716                     cm.setColumnWidth(ci, cw, true);
51717                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
51718                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
51719                     gv.updateSplitters();
51720                     gv.layout(false, true);
51721                 }
51722             }
51723
51724             if(initialRender){
51725                 lw.show();
51726                 mw.show();
51727             }
51728             //c.endMeasure();
51729         }, 10);
51730     },
51731
51732     onWindowResize : function(){
51733         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
51734             return;
51735         }
51736         this.layout();
51737     },
51738
51739     appendFooter : function(parentEl){
51740         return null;
51741     },
51742
51743     sortAscText : "Sort Ascending",
51744     sortDescText : "Sort Descending",
51745     lockText : "Lock Column",
51746     unlockText : "Unlock Column",
51747     columnsText : "Columns"
51748 });
51749
51750
51751 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
51752     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
51753     this.proxy.el.addClass('x-grid3-col-dd');
51754 };
51755
51756 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
51757     handleMouseDown : function(e){
51758
51759     },
51760
51761     callHandleMouseDown : function(e){
51762         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
51763     }
51764 });
51765 /*
51766  * Based on:
51767  * Ext JS Library 1.1.1
51768  * Copyright(c) 2006-2007, Ext JS, LLC.
51769  *
51770  * Originally Released Under LGPL - original licence link has changed is not relivant.
51771  *
51772  * Fork - LGPL
51773  * <script type="text/javascript">
51774  */
51775  
51776 // private
51777 // This is a support class used internally by the Grid components
51778 Roo.grid.SplitDragZone = function(grid, hd, hd2){
51779     this.grid = grid;
51780     this.view = grid.getView();
51781     this.proxy = this.view.resizeProxy;
51782     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
51783         "gridSplitters" + this.grid.getGridEl().id, {
51784         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
51785     });
51786     this.setHandleElId(Roo.id(hd));
51787     this.setOuterHandleElId(Roo.id(hd2));
51788     this.scroll = false;
51789 };
51790 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
51791     fly: Roo.Element.fly,
51792
51793     b4StartDrag : function(x, y){
51794         this.view.headersDisabled = true;
51795         this.proxy.setHeight(this.view.mainWrap.getHeight());
51796         var w = this.cm.getColumnWidth(this.cellIndex);
51797         var minw = Math.max(w-this.grid.minColumnWidth, 0);
51798         this.resetConstraints();
51799         this.setXConstraint(minw, 1000);
51800         this.setYConstraint(0, 0);
51801         this.minX = x - minw;
51802         this.maxX = x + 1000;
51803         this.startPos = x;
51804         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
51805     },
51806
51807
51808     handleMouseDown : function(e){
51809         ev = Roo.EventObject.setEvent(e);
51810         var t = this.fly(ev.getTarget());
51811         if(t.hasClass("x-grid-split")){
51812             this.cellIndex = this.view.getCellIndex(t.dom);
51813             this.split = t.dom;
51814             this.cm = this.grid.colModel;
51815             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
51816                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
51817             }
51818         }
51819     },
51820
51821     endDrag : function(e){
51822         this.view.headersDisabled = false;
51823         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
51824         var diff = endX - this.startPos;
51825         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
51826     },
51827
51828     autoOffset : function(){
51829         this.setDelta(0,0);
51830     }
51831 });/*
51832  * Based on:
51833  * Ext JS Library 1.1.1
51834  * Copyright(c) 2006-2007, Ext JS, LLC.
51835  *
51836  * Originally Released Under LGPL - original licence link has changed is not relivant.
51837  *
51838  * Fork - LGPL
51839  * <script type="text/javascript">
51840  */
51841  
51842 // private
51843 // This is a support class used internally by the Grid components
51844 Roo.grid.GridDragZone = function(grid, config){
51845     this.view = grid.getView();
51846     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
51847     if(this.view.lockedBody){
51848         this.setHandleElId(Roo.id(this.view.mainBody.dom));
51849         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
51850     }
51851     this.scroll = false;
51852     this.grid = grid;
51853     this.ddel = document.createElement('div');
51854     this.ddel.className = 'x-grid-dd-wrap';
51855 };
51856
51857 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
51858     ddGroup : "GridDD",
51859
51860     getDragData : function(e){
51861         var t = Roo.lib.Event.getTarget(e);
51862         var rowIndex = this.view.findRowIndex(t);
51863         var sm = this.grid.selModel;
51864             
51865         //Roo.log(rowIndex);
51866         
51867         if (sm.getSelectedCell) {
51868             // cell selection..
51869             if (!sm.getSelectedCell()) {
51870                 return false;
51871             }
51872             if (rowIndex != sm.getSelectedCell()[0]) {
51873                 return false;
51874             }
51875         
51876         }
51877         
51878         if(rowIndex !== false){
51879             
51880             // if editorgrid.. 
51881             
51882             
51883             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
51884                
51885             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
51886               //  
51887             //}
51888             if (e.hasModifier()){
51889                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
51890             }
51891             
51892             Roo.log("getDragData");
51893             
51894             return {
51895                 grid: this.grid,
51896                 ddel: this.ddel,
51897                 rowIndex: rowIndex,
51898                 selections:sm.getSelections ? sm.getSelections() : (
51899                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
51900                 )
51901             };
51902         }
51903         return false;
51904     },
51905
51906     onInitDrag : function(e){
51907         var data = this.dragData;
51908         this.ddel.innerHTML = this.grid.getDragDropText();
51909         this.proxy.update(this.ddel);
51910         // fire start drag?
51911     },
51912
51913     afterRepair : function(){
51914         this.dragging = false;
51915     },
51916
51917     getRepairXY : function(e, data){
51918         return false;
51919     },
51920
51921     onEndDrag : function(data, e){
51922         // fire end drag?
51923     },
51924
51925     onValidDrop : function(dd, e, id){
51926         // fire drag drop?
51927         this.hideProxy();
51928     },
51929
51930     beforeInvalidDrop : function(e, id){
51931
51932     }
51933 });/*
51934  * Based on:
51935  * Ext JS Library 1.1.1
51936  * Copyright(c) 2006-2007, Ext JS, LLC.
51937  *
51938  * Originally Released Under LGPL - original licence link has changed is not relivant.
51939  *
51940  * Fork - LGPL
51941  * <script type="text/javascript">
51942  */
51943  
51944
51945 /**
51946  * @class Roo.grid.ColumnModel
51947  * @extends Roo.util.Observable
51948  * This is the default implementation of a ColumnModel used by the Grid. It defines
51949  * the columns in the grid.
51950  * <br>Usage:<br>
51951  <pre><code>
51952  var colModel = new Roo.grid.ColumnModel([
51953         {header: "Ticker", width: 60, sortable: true, locked: true},
51954         {header: "Company Name", width: 150, sortable: true},
51955         {header: "Market Cap.", width: 100, sortable: true},
51956         {header: "$ Sales", width: 100, sortable: true, renderer: money},
51957         {header: "Employees", width: 100, sortable: true, resizable: false}
51958  ]);
51959  </code></pre>
51960  * <p>
51961  
51962  * The config options listed for this class are options which may appear in each
51963  * individual column definition.
51964  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
51965  * @constructor
51966  * @param {Object} config An Array of column config objects. See this class's
51967  * config objects for details.
51968 */
51969 Roo.grid.ColumnModel = function(config){
51970         /**
51971      * The config passed into the constructor
51972      */
51973     this.config = config;
51974     this.lookup = {};
51975
51976     // if no id, create one
51977     // if the column does not have a dataIndex mapping,
51978     // map it to the order it is in the config
51979     for(var i = 0, len = config.length; i < len; i++){
51980         var c = config[i];
51981         if(typeof c.dataIndex == "undefined"){
51982             c.dataIndex = i;
51983         }
51984         if(typeof c.renderer == "string"){
51985             c.renderer = Roo.util.Format[c.renderer];
51986         }
51987         if(typeof c.id == "undefined"){
51988             c.id = Roo.id();
51989         }
51990         if(c.editor && c.editor.xtype){
51991             c.editor  = Roo.factory(c.editor, Roo.grid);
51992         }
51993         if(c.editor && c.editor.isFormField){
51994             c.editor = new Roo.grid.GridEditor(c.editor);
51995         }
51996         this.lookup[c.id] = c;
51997     }
51998
51999     /**
52000      * The width of columns which have no width specified (defaults to 100)
52001      * @type Number
52002      */
52003     this.defaultWidth = 100;
52004
52005     /**
52006      * Default sortable of columns which have no sortable specified (defaults to false)
52007      * @type Boolean
52008      */
52009     this.defaultSortable = false;
52010
52011     this.addEvents({
52012         /**
52013              * @event widthchange
52014              * Fires when the width of a column changes.
52015              * @param {ColumnModel} this
52016              * @param {Number} columnIndex The column index
52017              * @param {Number} newWidth The new width
52018              */
52019             "widthchange": true,
52020         /**
52021              * @event headerchange
52022              * Fires when the text of a header changes.
52023              * @param {ColumnModel} this
52024              * @param {Number} columnIndex The column index
52025              * @param {Number} newText The new header text
52026              */
52027             "headerchange": true,
52028         /**
52029              * @event hiddenchange
52030              * Fires when a column is hidden or "unhidden".
52031              * @param {ColumnModel} this
52032              * @param {Number} columnIndex The column index
52033              * @param {Boolean} hidden true if hidden, false otherwise
52034              */
52035             "hiddenchange": true,
52036             /**
52037          * @event columnmoved
52038          * Fires when a column is moved.
52039          * @param {ColumnModel} this
52040          * @param {Number} oldIndex
52041          * @param {Number} newIndex
52042          */
52043         "columnmoved" : true,
52044         /**
52045          * @event columlockchange
52046          * Fires when a column's locked state is changed
52047          * @param {ColumnModel} this
52048          * @param {Number} colIndex
52049          * @param {Boolean} locked true if locked
52050          */
52051         "columnlockchange" : true
52052     });
52053     Roo.grid.ColumnModel.superclass.constructor.call(this);
52054 };
52055 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
52056     /**
52057      * @cfg {String} header The header text to display in the Grid view.
52058      */
52059     /**
52060      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
52061      * {@link Roo.data.Record} definition from which to draw the column's value. If not
52062      * specified, the column's index is used as an index into the Record's data Array.
52063      */
52064     /**
52065      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
52066      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
52067      */
52068     /**
52069      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
52070      * Defaults to the value of the {@link #defaultSortable} property.
52071      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
52072      */
52073     /**
52074      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
52075      */
52076     /**
52077      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
52078      */
52079     /**
52080      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
52081      */
52082     /**
52083      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
52084      */
52085     /**
52086      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
52087      * given the cell's data value. See {@link #setRenderer}. If not specified, the
52088      * default renderer uses the raw data value.
52089      */
52090        /**
52091      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
52092      */
52093     /**
52094      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
52095      */
52096
52097     /**
52098      * Returns the id of the column at the specified index.
52099      * @param {Number} index The column index
52100      * @return {String} the id
52101      */
52102     getColumnId : function(index){
52103         return this.config[index].id;
52104     },
52105
52106     /**
52107      * Returns the column for a specified id.
52108      * @param {String} id The column id
52109      * @return {Object} the column
52110      */
52111     getColumnById : function(id){
52112         return this.lookup[id];
52113     },
52114
52115     
52116     /**
52117      * Returns the column for a specified dataIndex.
52118      * @param {String} dataIndex The column dataIndex
52119      * @return {Object|Boolean} the column or false if not found
52120      */
52121     getColumnByDataIndex: function(dataIndex){
52122         var index = this.findColumnIndex(dataIndex);
52123         return index > -1 ? this.config[index] : false;
52124     },
52125     
52126     /**
52127      * Returns the index for a specified column id.
52128      * @param {String} id The column id
52129      * @return {Number} the index, or -1 if not found
52130      */
52131     getIndexById : function(id){
52132         for(var i = 0, len = this.config.length; i < len; i++){
52133             if(this.config[i].id == id){
52134                 return i;
52135             }
52136         }
52137         return -1;
52138     },
52139     
52140     /**
52141      * Returns the index for a specified column dataIndex.
52142      * @param {String} dataIndex The column dataIndex
52143      * @return {Number} the index, or -1 if not found
52144      */
52145     
52146     findColumnIndex : function(dataIndex){
52147         for(var i = 0, len = this.config.length; i < len; i++){
52148             if(this.config[i].dataIndex == dataIndex){
52149                 return i;
52150             }
52151         }
52152         return -1;
52153     },
52154     
52155     
52156     moveColumn : function(oldIndex, newIndex){
52157         var c = this.config[oldIndex];
52158         this.config.splice(oldIndex, 1);
52159         this.config.splice(newIndex, 0, c);
52160         this.dataMap = null;
52161         this.fireEvent("columnmoved", this, oldIndex, newIndex);
52162     },
52163
52164     isLocked : function(colIndex){
52165         return this.config[colIndex].locked === true;
52166     },
52167
52168     setLocked : function(colIndex, value, suppressEvent){
52169         if(this.isLocked(colIndex) == value){
52170             return;
52171         }
52172         this.config[colIndex].locked = value;
52173         if(!suppressEvent){
52174             this.fireEvent("columnlockchange", this, colIndex, value);
52175         }
52176     },
52177
52178     getTotalLockedWidth : function(){
52179         var totalWidth = 0;
52180         for(var i = 0; i < this.config.length; i++){
52181             if(this.isLocked(i) && !this.isHidden(i)){
52182                 this.totalWidth += this.getColumnWidth(i);
52183             }
52184         }
52185         return totalWidth;
52186     },
52187
52188     getLockedCount : function(){
52189         for(var i = 0, len = this.config.length; i < len; i++){
52190             if(!this.isLocked(i)){
52191                 return i;
52192             }
52193         }
52194     },
52195
52196     /**
52197      * Returns the number of columns.
52198      * @return {Number}
52199      */
52200     getColumnCount : function(visibleOnly){
52201         if(visibleOnly === true){
52202             var c = 0;
52203             for(var i = 0, len = this.config.length; i < len; i++){
52204                 if(!this.isHidden(i)){
52205                     c++;
52206                 }
52207             }
52208             return c;
52209         }
52210         return this.config.length;
52211     },
52212
52213     /**
52214      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
52215      * @param {Function} fn
52216      * @param {Object} scope (optional)
52217      * @return {Array} result
52218      */
52219     getColumnsBy : function(fn, scope){
52220         var r = [];
52221         for(var i = 0, len = this.config.length; i < len; i++){
52222             var c = this.config[i];
52223             if(fn.call(scope||this, c, i) === true){
52224                 r[r.length] = c;
52225             }
52226         }
52227         return r;
52228     },
52229
52230     /**
52231      * Returns true if the specified column is sortable.
52232      * @param {Number} col The column index
52233      * @return {Boolean}
52234      */
52235     isSortable : function(col){
52236         if(typeof this.config[col].sortable == "undefined"){
52237             return this.defaultSortable;
52238         }
52239         return this.config[col].sortable;
52240     },
52241
52242     /**
52243      * Returns the rendering (formatting) function defined for the column.
52244      * @param {Number} col The column index.
52245      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
52246      */
52247     getRenderer : function(col){
52248         if(!this.config[col].renderer){
52249             return Roo.grid.ColumnModel.defaultRenderer;
52250         }
52251         return this.config[col].renderer;
52252     },
52253
52254     /**
52255      * Sets the rendering (formatting) function for a column.
52256      * @param {Number} col The column index
52257      * @param {Function} fn The function to use to process the cell's raw data
52258      * to return HTML markup for the grid view. The render function is called with
52259      * the following parameters:<ul>
52260      * <li>Data value.</li>
52261      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
52262      * <li>css A CSS style string to apply to the table cell.</li>
52263      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
52264      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
52265      * <li>Row index</li>
52266      * <li>Column index</li>
52267      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
52268      */
52269     setRenderer : function(col, fn){
52270         this.config[col].renderer = fn;
52271     },
52272
52273     /**
52274      * Returns the width for the specified column.
52275      * @param {Number} col The column index
52276      * @return {Number}
52277      */
52278     getColumnWidth : function(col){
52279         return this.config[col].width * 1 || this.defaultWidth;
52280     },
52281
52282     /**
52283      * Sets the width for a column.
52284      * @param {Number} col The column index
52285      * @param {Number} width The new width
52286      */
52287     setColumnWidth : function(col, width, suppressEvent){
52288         this.config[col].width = width;
52289         this.totalWidth = null;
52290         if(!suppressEvent){
52291              this.fireEvent("widthchange", this, col, width);
52292         }
52293     },
52294
52295     /**
52296      * Returns the total width of all columns.
52297      * @param {Boolean} includeHidden True to include hidden column widths
52298      * @return {Number}
52299      */
52300     getTotalWidth : function(includeHidden){
52301         if(!this.totalWidth){
52302             this.totalWidth = 0;
52303             for(var i = 0, len = this.config.length; i < len; i++){
52304                 if(includeHidden || !this.isHidden(i)){
52305                     this.totalWidth += this.getColumnWidth(i);
52306                 }
52307             }
52308         }
52309         return this.totalWidth;
52310     },
52311
52312     /**
52313      * Returns the header for the specified column.
52314      * @param {Number} col The column index
52315      * @return {String}
52316      */
52317     getColumnHeader : function(col){
52318         return this.config[col].header;
52319     },
52320
52321     /**
52322      * Sets the header for a column.
52323      * @param {Number} col The column index
52324      * @param {String} header The new header
52325      */
52326     setColumnHeader : function(col, header){
52327         this.config[col].header = header;
52328         this.fireEvent("headerchange", this, col, header);
52329     },
52330
52331     /**
52332      * Returns the tooltip for the specified column.
52333      * @param {Number} col The column index
52334      * @return {String}
52335      */
52336     getColumnTooltip : function(col){
52337             return this.config[col].tooltip;
52338     },
52339     /**
52340      * Sets the tooltip for a column.
52341      * @param {Number} col The column index
52342      * @param {String} tooltip The new tooltip
52343      */
52344     setColumnTooltip : function(col, tooltip){
52345             this.config[col].tooltip = tooltip;
52346     },
52347
52348     /**
52349      * Returns the dataIndex for the specified column.
52350      * @param {Number} col The column index
52351      * @return {Number}
52352      */
52353     getDataIndex : function(col){
52354         return this.config[col].dataIndex;
52355     },
52356
52357     /**
52358      * Sets the dataIndex for a column.
52359      * @param {Number} col The column index
52360      * @param {Number} dataIndex The new dataIndex
52361      */
52362     setDataIndex : function(col, dataIndex){
52363         this.config[col].dataIndex = dataIndex;
52364     },
52365
52366     
52367     
52368     /**
52369      * Returns true if the cell is editable.
52370      * @param {Number} colIndex The column index
52371      * @param {Number} rowIndex The row index
52372      * @return {Boolean}
52373      */
52374     isCellEditable : function(colIndex, rowIndex){
52375         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
52376     },
52377
52378     /**
52379      * Returns the editor defined for the cell/column.
52380      * return false or null to disable editing.
52381      * @param {Number} colIndex The column index
52382      * @param {Number} rowIndex The row index
52383      * @return {Object}
52384      */
52385     getCellEditor : function(colIndex, rowIndex){
52386         return this.config[colIndex].editor;
52387     },
52388
52389     /**
52390      * Sets if a column is editable.
52391      * @param {Number} col The column index
52392      * @param {Boolean} editable True if the column is editable
52393      */
52394     setEditable : function(col, editable){
52395         this.config[col].editable = editable;
52396     },
52397
52398
52399     /**
52400      * Returns true if the column is hidden.
52401      * @param {Number} colIndex The column index
52402      * @return {Boolean}
52403      */
52404     isHidden : function(colIndex){
52405         return this.config[colIndex].hidden;
52406     },
52407
52408
52409     /**
52410      * Returns true if the column width cannot be changed
52411      */
52412     isFixed : function(colIndex){
52413         return this.config[colIndex].fixed;
52414     },
52415
52416     /**
52417      * Returns true if the column can be resized
52418      * @return {Boolean}
52419      */
52420     isResizable : function(colIndex){
52421         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
52422     },
52423     /**
52424      * Sets if a column is hidden.
52425      * @param {Number} colIndex The column index
52426      * @param {Boolean} hidden True if the column is hidden
52427      */
52428     setHidden : function(colIndex, hidden){
52429         this.config[colIndex].hidden = hidden;
52430         this.totalWidth = null;
52431         this.fireEvent("hiddenchange", this, colIndex, hidden);
52432     },
52433
52434     /**
52435      * Sets the editor for a column.
52436      * @param {Number} col The column index
52437      * @param {Object} editor The editor object
52438      */
52439     setEditor : function(col, editor){
52440         this.config[col].editor = editor;
52441     }
52442 });
52443
52444 Roo.grid.ColumnModel.defaultRenderer = function(value){
52445         if(typeof value == "string" && value.length < 1){
52446             return "&#160;";
52447         }
52448         return value;
52449 };
52450
52451 // Alias for backwards compatibility
52452 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
52453 /*
52454  * Based on:
52455  * Ext JS Library 1.1.1
52456  * Copyright(c) 2006-2007, Ext JS, LLC.
52457  *
52458  * Originally Released Under LGPL - original licence link has changed is not relivant.
52459  *
52460  * Fork - LGPL
52461  * <script type="text/javascript">
52462  */
52463
52464 /**
52465  * @class Roo.grid.AbstractSelectionModel
52466  * @extends Roo.util.Observable
52467  * Abstract base class for grid SelectionModels.  It provides the interface that should be
52468  * implemented by descendant classes.  This class should not be directly instantiated.
52469  * @constructor
52470  */
52471 Roo.grid.AbstractSelectionModel = function(){
52472     this.locked = false;
52473     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
52474 };
52475
52476 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
52477     /** @ignore Called by the grid automatically. Do not call directly. */
52478     init : function(grid){
52479         this.grid = grid;
52480         this.initEvents();
52481     },
52482
52483     /**
52484      * Locks the selections.
52485      */
52486     lock : function(){
52487         this.locked = true;
52488     },
52489
52490     /**
52491      * Unlocks the selections.
52492      */
52493     unlock : function(){
52494         this.locked = false;
52495     },
52496
52497     /**
52498      * Returns true if the selections are locked.
52499      * @return {Boolean}
52500      */
52501     isLocked : function(){
52502         return this.locked;
52503     }
52504 });/*
52505  * Based on:
52506  * Ext JS Library 1.1.1
52507  * Copyright(c) 2006-2007, Ext JS, LLC.
52508  *
52509  * Originally Released Under LGPL - original licence link has changed is not relivant.
52510  *
52511  * Fork - LGPL
52512  * <script type="text/javascript">
52513  */
52514 /**
52515  * @extends Roo.grid.AbstractSelectionModel
52516  * @class Roo.grid.RowSelectionModel
52517  * The default SelectionModel used by {@link Roo.grid.Grid}.
52518  * It supports multiple selections and keyboard selection/navigation. 
52519  * @constructor
52520  * @param {Object} config
52521  */
52522 Roo.grid.RowSelectionModel = function(config){
52523     Roo.apply(this, config);
52524     this.selections = new Roo.util.MixedCollection(false, function(o){
52525         return o.id;
52526     });
52527
52528     this.last = false;
52529     this.lastActive = false;
52530
52531     this.addEvents({
52532         /**
52533              * @event selectionchange
52534              * Fires when the selection changes
52535              * @param {SelectionModel} this
52536              */
52537             "selectionchange" : true,
52538         /**
52539              * @event afterselectionchange
52540              * Fires after the selection changes (eg. by key press or clicking)
52541              * @param {SelectionModel} this
52542              */
52543             "afterselectionchange" : true,
52544         /**
52545              * @event beforerowselect
52546              * Fires when a row is selected being selected, return false to cancel.
52547              * @param {SelectionModel} this
52548              * @param {Number} rowIndex The selected index
52549              * @param {Boolean} keepExisting False if other selections will be cleared
52550              */
52551             "beforerowselect" : true,
52552         /**
52553              * @event rowselect
52554              * Fires when a row is selected.
52555              * @param {SelectionModel} this
52556              * @param {Number} rowIndex The selected index
52557              * @param {Roo.data.Record} r The record
52558              */
52559             "rowselect" : true,
52560         /**
52561              * @event rowdeselect
52562              * Fires when a row is deselected.
52563              * @param {SelectionModel} this
52564              * @param {Number} rowIndex The selected index
52565              */
52566         "rowdeselect" : true
52567     });
52568     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
52569     this.locked = false;
52570 };
52571
52572 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
52573     /**
52574      * @cfg {Boolean} singleSelect
52575      * True to allow selection of only one row at a time (defaults to false)
52576      */
52577     singleSelect : false,
52578
52579     // private
52580     initEvents : function(){
52581
52582         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
52583             this.grid.on("mousedown", this.handleMouseDown, this);
52584         }else{ // allow click to work like normal
52585             this.grid.on("rowclick", this.handleDragableRowClick, this);
52586         }
52587
52588         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
52589             "up" : function(e){
52590                 if(!e.shiftKey){
52591                     this.selectPrevious(e.shiftKey);
52592                 }else if(this.last !== false && this.lastActive !== false){
52593                     var last = this.last;
52594                     this.selectRange(this.last,  this.lastActive-1);
52595                     this.grid.getView().focusRow(this.lastActive);
52596                     if(last !== false){
52597                         this.last = last;
52598                     }
52599                 }else{
52600                     this.selectFirstRow();
52601                 }
52602                 this.fireEvent("afterselectionchange", this);
52603             },
52604             "down" : function(e){
52605                 if(!e.shiftKey){
52606                     this.selectNext(e.shiftKey);
52607                 }else if(this.last !== false && this.lastActive !== false){
52608                     var last = this.last;
52609                     this.selectRange(this.last,  this.lastActive+1);
52610                     this.grid.getView().focusRow(this.lastActive);
52611                     if(last !== false){
52612                         this.last = last;
52613                     }
52614                 }else{
52615                     this.selectFirstRow();
52616                 }
52617                 this.fireEvent("afterselectionchange", this);
52618             },
52619             scope: this
52620         });
52621
52622         var view = this.grid.view;
52623         view.on("refresh", this.onRefresh, this);
52624         view.on("rowupdated", this.onRowUpdated, this);
52625         view.on("rowremoved", this.onRemove, this);
52626     },
52627
52628     // private
52629     onRefresh : function(){
52630         var ds = this.grid.dataSource, i, v = this.grid.view;
52631         var s = this.selections;
52632         s.each(function(r){
52633             if((i = ds.indexOfId(r.id)) != -1){
52634                 v.onRowSelect(i);
52635             }else{
52636                 s.remove(r);
52637             }
52638         });
52639     },
52640
52641     // private
52642     onRemove : function(v, index, r){
52643         this.selections.remove(r);
52644     },
52645
52646     // private
52647     onRowUpdated : function(v, index, r){
52648         if(this.isSelected(r)){
52649             v.onRowSelect(index);
52650         }
52651     },
52652
52653     /**
52654      * Select records.
52655      * @param {Array} records The records to select
52656      * @param {Boolean} keepExisting (optional) True to keep existing selections
52657      */
52658     selectRecords : function(records, keepExisting){
52659         if(!keepExisting){
52660             this.clearSelections();
52661         }
52662         var ds = this.grid.dataSource;
52663         for(var i = 0, len = records.length; i < len; i++){
52664             this.selectRow(ds.indexOf(records[i]), true);
52665         }
52666     },
52667
52668     /**
52669      * Gets the number of selected rows.
52670      * @return {Number}
52671      */
52672     getCount : function(){
52673         return this.selections.length;
52674     },
52675
52676     /**
52677      * Selects the first row in the grid.
52678      */
52679     selectFirstRow : function(){
52680         this.selectRow(0);
52681     },
52682
52683     /**
52684      * Select the last row.
52685      * @param {Boolean} keepExisting (optional) True to keep existing selections
52686      */
52687     selectLastRow : function(keepExisting){
52688         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
52689     },
52690
52691     /**
52692      * Selects the row immediately following the last selected row.
52693      * @param {Boolean} keepExisting (optional) True to keep existing selections
52694      */
52695     selectNext : function(keepExisting){
52696         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
52697             this.selectRow(this.last+1, keepExisting);
52698             this.grid.getView().focusRow(this.last);
52699         }
52700     },
52701
52702     /**
52703      * Selects the row that precedes the last selected row.
52704      * @param {Boolean} keepExisting (optional) True to keep existing selections
52705      */
52706     selectPrevious : function(keepExisting){
52707         if(this.last){
52708             this.selectRow(this.last-1, keepExisting);
52709             this.grid.getView().focusRow(this.last);
52710         }
52711     },
52712
52713     /**
52714      * Returns the selected records
52715      * @return {Array} Array of selected records
52716      */
52717     getSelections : function(){
52718         return [].concat(this.selections.items);
52719     },
52720
52721     /**
52722      * Returns the first selected record.
52723      * @return {Record}
52724      */
52725     getSelected : function(){
52726         return this.selections.itemAt(0);
52727     },
52728
52729
52730     /**
52731      * Clears all selections.
52732      */
52733     clearSelections : function(fast){
52734         if(this.locked) return;
52735         if(fast !== true){
52736             var ds = this.grid.dataSource;
52737             var s = this.selections;
52738             s.each(function(r){
52739                 this.deselectRow(ds.indexOfId(r.id));
52740             }, this);
52741             s.clear();
52742         }else{
52743             this.selections.clear();
52744         }
52745         this.last = false;
52746     },
52747
52748
52749     /**
52750      * Selects all rows.
52751      */
52752     selectAll : function(){
52753         if(this.locked) return;
52754         this.selections.clear();
52755         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
52756             this.selectRow(i, true);
52757         }
52758     },
52759
52760     /**
52761      * Returns True if there is a selection.
52762      * @return {Boolean}
52763      */
52764     hasSelection : function(){
52765         return this.selections.length > 0;
52766     },
52767
52768     /**
52769      * Returns True if the specified row is selected.
52770      * @param {Number/Record} record The record or index of the record to check
52771      * @return {Boolean}
52772      */
52773     isSelected : function(index){
52774         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
52775         return (r && this.selections.key(r.id) ? true : false);
52776     },
52777
52778     /**
52779      * Returns True if the specified record id is selected.
52780      * @param {String} id The id of record to check
52781      * @return {Boolean}
52782      */
52783     isIdSelected : function(id){
52784         return (this.selections.key(id) ? true : false);
52785     },
52786
52787     // private
52788     handleMouseDown : function(e, t){
52789         var view = this.grid.getView(), rowIndex;
52790         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
52791             return;
52792         };
52793         if(e.shiftKey && this.last !== false){
52794             var last = this.last;
52795             this.selectRange(last, rowIndex, e.ctrlKey);
52796             this.last = last; // reset the last
52797             view.focusRow(rowIndex);
52798         }else{
52799             var isSelected = this.isSelected(rowIndex);
52800             if(e.button !== 0 && isSelected){
52801                 view.focusRow(rowIndex);
52802             }else if(e.ctrlKey && isSelected){
52803                 this.deselectRow(rowIndex);
52804             }else if(!isSelected){
52805                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
52806                 view.focusRow(rowIndex);
52807             }
52808         }
52809         this.fireEvent("afterselectionchange", this);
52810     },
52811     // private
52812     handleDragableRowClick :  function(grid, rowIndex, e) 
52813     {
52814         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
52815             this.selectRow(rowIndex, false);
52816             grid.view.focusRow(rowIndex);
52817              this.fireEvent("afterselectionchange", this);
52818         }
52819     },
52820     
52821     /**
52822      * Selects multiple rows.
52823      * @param {Array} rows Array of the indexes of the row to select
52824      * @param {Boolean} keepExisting (optional) True to keep existing selections
52825      */
52826     selectRows : function(rows, keepExisting){
52827         if(!keepExisting){
52828             this.clearSelections();
52829         }
52830         for(var i = 0, len = rows.length; i < len; i++){
52831             this.selectRow(rows[i], true);
52832         }
52833     },
52834
52835     /**
52836      * Selects a range of rows. All rows in between startRow and endRow are also selected.
52837      * @param {Number} startRow The index of the first row in the range
52838      * @param {Number} endRow The index of the last row in the range
52839      * @param {Boolean} keepExisting (optional) True to retain existing selections
52840      */
52841     selectRange : function(startRow, endRow, keepExisting){
52842         if(this.locked) return;
52843         if(!keepExisting){
52844             this.clearSelections();
52845         }
52846         if(startRow <= endRow){
52847             for(var i = startRow; i <= endRow; i++){
52848                 this.selectRow(i, true);
52849             }
52850         }else{
52851             for(var i = startRow; i >= endRow; i--){
52852                 this.selectRow(i, true);
52853             }
52854         }
52855     },
52856
52857     /**
52858      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
52859      * @param {Number} startRow The index of the first row in the range
52860      * @param {Number} endRow The index of the last row in the range
52861      */
52862     deselectRange : function(startRow, endRow, preventViewNotify){
52863         if(this.locked) return;
52864         for(var i = startRow; i <= endRow; i++){
52865             this.deselectRow(i, preventViewNotify);
52866         }
52867     },
52868
52869     /**
52870      * Selects a row.
52871      * @param {Number} row The index of the row to select
52872      * @param {Boolean} keepExisting (optional) True to keep existing selections
52873      */
52874     selectRow : function(index, keepExisting, preventViewNotify){
52875         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
52876         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
52877             if(!keepExisting || this.singleSelect){
52878                 this.clearSelections();
52879             }
52880             var r = this.grid.dataSource.getAt(index);
52881             this.selections.add(r);
52882             this.last = this.lastActive = index;
52883             if(!preventViewNotify){
52884                 this.grid.getView().onRowSelect(index);
52885             }
52886             this.fireEvent("rowselect", this, index, r);
52887             this.fireEvent("selectionchange", this);
52888         }
52889     },
52890
52891     /**
52892      * Deselects a row.
52893      * @param {Number} row The index of the row to deselect
52894      */
52895     deselectRow : function(index, preventViewNotify){
52896         if(this.locked) return;
52897         if(this.last == index){
52898             this.last = false;
52899         }
52900         if(this.lastActive == index){
52901             this.lastActive = false;
52902         }
52903         var r = this.grid.dataSource.getAt(index);
52904         this.selections.remove(r);
52905         if(!preventViewNotify){
52906             this.grid.getView().onRowDeselect(index);
52907         }
52908         this.fireEvent("rowdeselect", this, index);
52909         this.fireEvent("selectionchange", this);
52910     },
52911
52912     // private
52913     restoreLast : function(){
52914         if(this._last){
52915             this.last = this._last;
52916         }
52917     },
52918
52919     // private
52920     acceptsNav : function(row, col, cm){
52921         return !cm.isHidden(col) && cm.isCellEditable(col, row);
52922     },
52923
52924     // private
52925     onEditorKey : function(field, e){
52926         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
52927         if(k == e.TAB){
52928             e.stopEvent();
52929             ed.completeEdit();
52930             if(e.shiftKey){
52931                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
52932             }else{
52933                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
52934             }
52935         }else if(k == e.ENTER && !e.ctrlKey){
52936             e.stopEvent();
52937             ed.completeEdit();
52938             if(e.shiftKey){
52939                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
52940             }else{
52941                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
52942             }
52943         }else if(k == e.ESC){
52944             ed.cancelEdit();
52945         }
52946         if(newCell){
52947             g.startEditing(newCell[0], newCell[1]);
52948         }
52949     }
52950 });/*
52951  * Based on:
52952  * Ext JS Library 1.1.1
52953  * Copyright(c) 2006-2007, Ext JS, LLC.
52954  *
52955  * Originally Released Under LGPL - original licence link has changed is not relivant.
52956  *
52957  * Fork - LGPL
52958  * <script type="text/javascript">
52959  */
52960 /**
52961  * @class Roo.grid.CellSelectionModel
52962  * @extends Roo.grid.AbstractSelectionModel
52963  * This class provides the basic implementation for cell selection in a grid.
52964  * @constructor
52965  * @param {Object} config The object containing the configuration of this model.
52966  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
52967  */
52968 Roo.grid.CellSelectionModel = function(config){
52969     Roo.apply(this, config);
52970
52971     this.selection = null;
52972
52973     this.addEvents({
52974         /**
52975              * @event beforerowselect
52976              * Fires before a cell is selected.
52977              * @param {SelectionModel} this
52978              * @param {Number} rowIndex The selected row index
52979              * @param {Number} colIndex The selected cell index
52980              */
52981             "beforecellselect" : true,
52982         /**
52983              * @event cellselect
52984              * Fires when a cell is selected.
52985              * @param {SelectionModel} this
52986              * @param {Number} rowIndex The selected row index
52987              * @param {Number} colIndex The selected cell index
52988              */
52989             "cellselect" : true,
52990         /**
52991              * @event selectionchange
52992              * Fires when the active selection changes.
52993              * @param {SelectionModel} this
52994              * @param {Object} selection null for no selection or an object (o) with two properties
52995                 <ul>
52996                 <li>o.record: the record object for the row the selection is in</li>
52997                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
52998                 </ul>
52999              */
53000             "selectionchange" : true,
53001         /**
53002              * @event tabend
53003              * Fires when the tab (or enter) was pressed on the last editable cell
53004              * You can use this to trigger add new row.
53005              * @param {SelectionModel} this
53006              */
53007             "tabend" : true,
53008          /**
53009              * @event beforeeditnext
53010              * Fires before the next editable sell is made active
53011              * You can use this to skip to another cell or fire the tabend
53012              *    if you set cell to false
53013              * @param {Object} eventdata object : { cell : [ row, col ] } 
53014              */
53015             "beforeeditnext" : true
53016     });
53017     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
53018 };
53019
53020 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
53021     
53022     enter_is_tab: false,
53023
53024     /** @ignore */
53025     initEvents : function(){
53026         this.grid.on("mousedown", this.handleMouseDown, this);
53027         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
53028         var view = this.grid.view;
53029         view.on("refresh", this.onViewChange, this);
53030         view.on("rowupdated", this.onRowUpdated, this);
53031         view.on("beforerowremoved", this.clearSelections, this);
53032         view.on("beforerowsinserted", this.clearSelections, this);
53033         if(this.grid.isEditor){
53034             this.grid.on("beforeedit", this.beforeEdit,  this);
53035         }
53036     },
53037
53038         //private
53039     beforeEdit : function(e){
53040         this.select(e.row, e.column, false, true, e.record);
53041     },
53042
53043         //private
53044     onRowUpdated : function(v, index, r){
53045         if(this.selection && this.selection.record == r){
53046             v.onCellSelect(index, this.selection.cell[1]);
53047         }
53048     },
53049
53050         //private
53051     onViewChange : function(){
53052         this.clearSelections(true);
53053     },
53054
53055         /**
53056          * Returns the currently selected cell,.
53057          * @return {Array} The selected cell (row, column) or null if none selected.
53058          */
53059     getSelectedCell : function(){
53060         return this.selection ? this.selection.cell : null;
53061     },
53062
53063     /**
53064      * Clears all selections.
53065      * @param {Boolean} true to prevent the gridview from being notified about the change.
53066      */
53067     clearSelections : function(preventNotify){
53068         var s = this.selection;
53069         if(s){
53070             if(preventNotify !== true){
53071                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
53072             }
53073             this.selection = null;
53074             this.fireEvent("selectionchange", this, null);
53075         }
53076     },
53077
53078     /**
53079      * Returns true if there is a selection.
53080      * @return {Boolean}
53081      */
53082     hasSelection : function(){
53083         return this.selection ? true : false;
53084     },
53085
53086     /** @ignore */
53087     handleMouseDown : function(e, t){
53088         var v = this.grid.getView();
53089         if(this.isLocked()){
53090             return;
53091         };
53092         var row = v.findRowIndex(t);
53093         var cell = v.findCellIndex(t);
53094         if(row !== false && cell !== false){
53095             this.select(row, cell);
53096         }
53097     },
53098
53099     /**
53100      * Selects a cell.
53101      * @param {Number} rowIndex
53102      * @param {Number} collIndex
53103      */
53104     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
53105         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
53106             this.clearSelections();
53107             r = r || this.grid.dataSource.getAt(rowIndex);
53108             this.selection = {
53109                 record : r,
53110                 cell : [rowIndex, colIndex]
53111             };
53112             if(!preventViewNotify){
53113                 var v = this.grid.getView();
53114                 v.onCellSelect(rowIndex, colIndex);
53115                 if(preventFocus !== true){
53116                     v.focusCell(rowIndex, colIndex);
53117                 }
53118             }
53119             this.fireEvent("cellselect", this, rowIndex, colIndex);
53120             this.fireEvent("selectionchange", this, this.selection);
53121         }
53122     },
53123
53124         //private
53125     isSelectable : function(rowIndex, colIndex, cm){
53126         return !cm.isHidden(colIndex);
53127     },
53128
53129     /** @ignore */
53130     handleKeyDown : function(e){
53131         //Roo.log('Cell Sel Model handleKeyDown');
53132         if(!e.isNavKeyPress()){
53133             return;
53134         }
53135         var g = this.grid, s = this.selection;
53136         if(!s){
53137             e.stopEvent();
53138             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
53139             if(cell){
53140                 this.select(cell[0], cell[1]);
53141             }
53142             return;
53143         }
53144         var sm = this;
53145         var walk = function(row, col, step){
53146             return g.walkCells(row, col, step, sm.isSelectable,  sm);
53147         };
53148         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
53149         var newCell;
53150
53151       
53152
53153         switch(k){
53154             case e.TAB:
53155                 // handled by onEditorKey
53156                 if (g.isEditor && g.editing) {
53157                     return;
53158                 }
53159                 if(e.shiftKey) {
53160                     newCell = walk(r, c-1, -1);
53161                 } else {
53162                     newCell = walk(r, c+1, 1);
53163                 }
53164                 break;
53165             
53166             case e.DOWN:
53167                newCell = walk(r+1, c, 1);
53168                 break;
53169             
53170             case e.UP:
53171                 newCell = walk(r-1, c, -1);
53172                 break;
53173             
53174             case e.RIGHT:
53175                 newCell = walk(r, c+1, 1);
53176                 break;
53177             
53178             case e.LEFT:
53179                 newCell = walk(r, c-1, -1);
53180                 break;
53181             
53182             case e.ENTER:
53183                 
53184                 if(g.isEditor && !g.editing){
53185                    g.startEditing(r, c);
53186                    e.stopEvent();
53187                    return;
53188                 }
53189                 
53190                 
53191              break;
53192         };
53193         if(newCell){
53194             this.select(newCell[0], newCell[1]);
53195             e.stopEvent();
53196             
53197         }
53198     },
53199
53200     acceptsNav : function(row, col, cm){
53201         return !cm.isHidden(col) && cm.isCellEditable(col, row);
53202     },
53203     /**
53204      * Selects a cell.
53205      * @param {Number} field (not used) - as it's normally used as a listener
53206      * @param {Number} e - event - fake it by using
53207      *
53208      * var e = Roo.EventObjectImpl.prototype;
53209      * e.keyCode = e.TAB
53210      *
53211      * 
53212      */
53213     onEditorKey : function(field, e){
53214         
53215         var k = e.getKey(),
53216             newCell,
53217             g = this.grid,
53218             ed = g.activeEditor,
53219             forward = false;
53220         ///Roo.log('onEditorKey' + k);
53221         
53222         
53223         if (this.enter_is_tab && k == e.ENTER) {
53224             k = e.TAB;
53225         }
53226         
53227         if(k == e.TAB){
53228             if(e.shiftKey){
53229                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
53230             }else{
53231                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53232                 forward = true;
53233             }
53234             
53235             e.stopEvent();
53236             
53237         } else if(k == e.ENTER &&  !e.ctrlKey){
53238             ed.completeEdit();
53239             e.stopEvent();
53240             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53241         
53242                 } else if(k == e.ESC){
53243             ed.cancelEdit();
53244         }
53245                 
53246         if (newCell) {
53247             var ecall = { cell : newCell, forward : forward };
53248             this.fireEvent('beforeeditnext', ecall );
53249             newCell = ecall.cell;
53250                         forward = ecall.forward;
53251         }
53252                 
53253         if(newCell){
53254             //Roo.log('next cell after edit');
53255             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
53256         } else if (forward) {
53257             // tabbed past last
53258             this.fireEvent.defer(100, this, ['tabend',this]);
53259         }
53260     }
53261 });/*
53262  * Based on:
53263  * Ext JS Library 1.1.1
53264  * Copyright(c) 2006-2007, Ext JS, LLC.
53265  *
53266  * Originally Released Under LGPL - original licence link has changed is not relivant.
53267  *
53268  * Fork - LGPL
53269  * <script type="text/javascript">
53270  */
53271  
53272 /**
53273  * @class Roo.grid.EditorGrid
53274  * @extends Roo.grid.Grid
53275  * Class for creating and editable grid.
53276  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
53277  * The container MUST have some type of size defined for the grid to fill. The container will be 
53278  * automatically set to position relative if it isn't already.
53279  * @param {Object} dataSource The data model to bind to
53280  * @param {Object} colModel The column model with info about this grid's columns
53281  */
53282 Roo.grid.EditorGrid = function(container, config){
53283     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
53284     this.getGridEl().addClass("xedit-grid");
53285
53286     if(!this.selModel){
53287         this.selModel = new Roo.grid.CellSelectionModel();
53288     }
53289
53290     this.activeEditor = null;
53291
53292         this.addEvents({
53293             /**
53294              * @event beforeedit
53295              * Fires before cell editing is triggered. The edit event object has the following properties <br />
53296              * <ul style="padding:5px;padding-left:16px;">
53297              * <li>grid - This grid</li>
53298              * <li>record - The record being edited</li>
53299              * <li>field - The field name being edited</li>
53300              * <li>value - The value for the field being edited.</li>
53301              * <li>row - The grid row index</li>
53302              * <li>column - The grid column index</li>
53303              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
53304              * </ul>
53305              * @param {Object} e An edit event (see above for description)
53306              */
53307             "beforeedit" : true,
53308             /**
53309              * @event afteredit
53310              * Fires after a cell is edited. <br />
53311              * <ul style="padding:5px;padding-left:16px;">
53312              * <li>grid - This grid</li>
53313              * <li>record - The record being edited</li>
53314              * <li>field - The field name being edited</li>
53315              * <li>value - The value being set</li>
53316              * <li>originalValue - The original value for the field, before the edit.</li>
53317              * <li>row - The grid row index</li>
53318              * <li>column - The grid column index</li>
53319              * </ul>
53320              * @param {Object} e An edit event (see above for description)
53321              */
53322             "afteredit" : true,
53323             /**
53324              * @event validateedit
53325              * Fires after a cell is edited, but before the value is set in the record. 
53326          * You can use this to modify the value being set in the field, Return false
53327              * to cancel the change. The edit event object has the following properties <br />
53328              * <ul style="padding:5px;padding-left:16px;">
53329          * <li>editor - This editor</li>
53330              * <li>grid - This grid</li>
53331              * <li>record - The record being edited</li>
53332              * <li>field - The field name being edited</li>
53333              * <li>value - The value being set</li>
53334              * <li>originalValue - The original value for the field, before the edit.</li>
53335              * <li>row - The grid row index</li>
53336              * <li>column - The grid column index</li>
53337              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
53338              * </ul>
53339              * @param {Object} e An edit event (see above for description)
53340              */
53341             "validateedit" : true
53342         });
53343     this.on("bodyscroll", this.stopEditing,  this);
53344     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
53345 };
53346
53347 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
53348     /**
53349      * @cfg {Number} clicksToEdit
53350      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
53351      */
53352     clicksToEdit: 2,
53353
53354     // private
53355     isEditor : true,
53356     // private
53357     trackMouseOver: false, // causes very odd FF errors
53358
53359     onCellDblClick : function(g, row, col){
53360         this.startEditing(row, col);
53361     },
53362
53363     onEditComplete : function(ed, value, startValue){
53364         this.editing = false;
53365         this.activeEditor = null;
53366         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
53367         var r = ed.record;
53368         var field = this.colModel.getDataIndex(ed.col);
53369         var e = {
53370             grid: this,
53371             record: r,
53372             field: field,
53373             originalValue: startValue,
53374             value: value,
53375             row: ed.row,
53376             column: ed.col,
53377             cancel:false,
53378             editor: ed
53379         };
53380         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
53381         cell.show();
53382           
53383         if(String(value) !== String(startValue)){
53384             
53385             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
53386                 r.set(field, e.value);
53387                 // if we are dealing with a combo box..
53388                 // then we also set the 'name' colum to be the displayField
53389                 if (ed.field.displayField && ed.field.name) {
53390                     r.set(ed.field.name, ed.field.el.dom.value);
53391                 }
53392                 
53393                 delete e.cancel; //?? why!!!
53394                 this.fireEvent("afteredit", e);
53395             }
53396         } else {
53397             this.fireEvent("afteredit", e); // always fire it!
53398         }
53399         this.view.focusCell(ed.row, ed.col);
53400     },
53401
53402     /**
53403      * Starts editing the specified for the specified row/column
53404      * @param {Number} rowIndex
53405      * @param {Number} colIndex
53406      */
53407     startEditing : function(row, col){
53408         this.stopEditing();
53409         if(this.colModel.isCellEditable(col, row)){
53410             this.view.ensureVisible(row, col, true);
53411           
53412             var r = this.dataSource.getAt(row);
53413             var field = this.colModel.getDataIndex(col);
53414             var cell = Roo.get(this.view.getCell(row,col));
53415             var e = {
53416                 grid: this,
53417                 record: r,
53418                 field: field,
53419                 value: r.data[field],
53420                 row: row,
53421                 column: col,
53422                 cancel:false 
53423             };
53424             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
53425                 this.editing = true;
53426                 var ed = this.colModel.getCellEditor(col, row);
53427                 
53428                 if (!ed) {
53429                     return;
53430                 }
53431                 if(!ed.rendered){
53432                     ed.render(ed.parentEl || document.body);
53433                 }
53434                 ed.field.reset();
53435                
53436                 cell.hide();
53437                 
53438                 (function(){ // complex but required for focus issues in safari, ie and opera
53439                     ed.row = row;
53440                     ed.col = col;
53441                     ed.record = r;
53442                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
53443                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
53444                     this.activeEditor = ed;
53445                     var v = r.data[field];
53446                     ed.startEdit(this.view.getCell(row, col), v);
53447                     // combo's with 'displayField and name set
53448                     if (ed.field.displayField && ed.field.name) {
53449                         ed.field.el.dom.value = r.data[ed.field.name];
53450                     }
53451                     
53452                     
53453                 }).defer(50, this);
53454             }
53455         }
53456     },
53457         
53458     /**
53459      * Stops any active editing
53460      */
53461     stopEditing : function(){
53462         if(this.activeEditor){
53463             this.activeEditor.completeEdit();
53464         }
53465         this.activeEditor = null;
53466     },
53467         
53468          /**
53469      * Called to get grid's drag proxy text, by default returns this.ddText.
53470      * @return {String}
53471      */
53472     getDragDropText : function(){
53473         var count = this.selModel.getSelectedCell() ? 1 : 0;
53474         return String.format(this.ddText, count, count == 1 ? '' : 's');
53475     }
53476         
53477 });/*
53478  * Based on:
53479  * Ext JS Library 1.1.1
53480  * Copyright(c) 2006-2007, Ext JS, LLC.
53481  *
53482  * Originally Released Under LGPL - original licence link has changed is not relivant.
53483  *
53484  * Fork - LGPL
53485  * <script type="text/javascript">
53486  */
53487
53488 // private - not really -- you end up using it !
53489 // This is a support class used internally by the Grid components
53490
53491 /**
53492  * @class Roo.grid.GridEditor
53493  * @extends Roo.Editor
53494  * Class for creating and editable grid elements.
53495  * @param {Object} config any settings (must include field)
53496  */
53497 Roo.grid.GridEditor = function(field, config){
53498     if (!config && field.field) {
53499         config = field;
53500         field = Roo.factory(config.field, Roo.form);
53501     }
53502     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
53503     field.monitorTab = false;
53504 };
53505
53506 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
53507     
53508     /**
53509      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
53510      */
53511     
53512     alignment: "tl-tl",
53513     autoSize: "width",
53514     hideEl : false,
53515     cls: "x-small-editor x-grid-editor",
53516     shim:false,
53517     shadow:"frame"
53518 });/*
53519  * Based on:
53520  * Ext JS Library 1.1.1
53521  * Copyright(c) 2006-2007, Ext JS, LLC.
53522  *
53523  * Originally Released Under LGPL - original licence link has changed is not relivant.
53524  *
53525  * Fork - LGPL
53526  * <script type="text/javascript">
53527  */
53528   
53529
53530   
53531 Roo.grid.PropertyRecord = Roo.data.Record.create([
53532     {name:'name',type:'string'},  'value'
53533 ]);
53534
53535
53536 Roo.grid.PropertyStore = function(grid, source){
53537     this.grid = grid;
53538     this.store = new Roo.data.Store({
53539         recordType : Roo.grid.PropertyRecord
53540     });
53541     this.store.on('update', this.onUpdate,  this);
53542     if(source){
53543         this.setSource(source);
53544     }
53545     Roo.grid.PropertyStore.superclass.constructor.call(this);
53546 };
53547
53548
53549
53550 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
53551     setSource : function(o){
53552         this.source = o;
53553         this.store.removeAll();
53554         var data = [];
53555         for(var k in o){
53556             if(this.isEditableValue(o[k])){
53557                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
53558             }
53559         }
53560         this.store.loadRecords({records: data}, {}, true);
53561     },
53562
53563     onUpdate : function(ds, record, type){
53564         if(type == Roo.data.Record.EDIT){
53565             var v = record.data['value'];
53566             var oldValue = record.modified['value'];
53567             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
53568                 this.source[record.id] = v;
53569                 record.commit();
53570                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
53571             }else{
53572                 record.reject();
53573             }
53574         }
53575     },
53576
53577     getProperty : function(row){
53578        return this.store.getAt(row);
53579     },
53580
53581     isEditableValue: function(val){
53582         if(val && val instanceof Date){
53583             return true;
53584         }else if(typeof val == 'object' || typeof val == 'function'){
53585             return false;
53586         }
53587         return true;
53588     },
53589
53590     setValue : function(prop, value){
53591         this.source[prop] = value;
53592         this.store.getById(prop).set('value', value);
53593     },
53594
53595     getSource : function(){
53596         return this.source;
53597     }
53598 });
53599
53600 Roo.grid.PropertyColumnModel = function(grid, store){
53601     this.grid = grid;
53602     var g = Roo.grid;
53603     g.PropertyColumnModel.superclass.constructor.call(this, [
53604         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
53605         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
53606     ]);
53607     this.store = store;
53608     this.bselect = Roo.DomHelper.append(document.body, {
53609         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
53610             {tag: 'option', value: 'true', html: 'true'},
53611             {tag: 'option', value: 'false', html: 'false'}
53612         ]
53613     });
53614     Roo.id(this.bselect);
53615     var f = Roo.form;
53616     this.editors = {
53617         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
53618         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
53619         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
53620         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
53621         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
53622     };
53623     this.renderCellDelegate = this.renderCell.createDelegate(this);
53624     this.renderPropDelegate = this.renderProp.createDelegate(this);
53625 };
53626
53627 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
53628     
53629     
53630     nameText : 'Name',
53631     valueText : 'Value',
53632     
53633     dateFormat : 'm/j/Y',
53634     
53635     
53636     renderDate : function(dateVal){
53637         return dateVal.dateFormat(this.dateFormat);
53638     },
53639
53640     renderBool : function(bVal){
53641         return bVal ? 'true' : 'false';
53642     },
53643
53644     isCellEditable : function(colIndex, rowIndex){
53645         return colIndex == 1;
53646     },
53647
53648     getRenderer : function(col){
53649         return col == 1 ?
53650             this.renderCellDelegate : this.renderPropDelegate;
53651     },
53652
53653     renderProp : function(v){
53654         return this.getPropertyName(v);
53655     },
53656
53657     renderCell : function(val){
53658         var rv = val;
53659         if(val instanceof Date){
53660             rv = this.renderDate(val);
53661         }else if(typeof val == 'boolean'){
53662             rv = this.renderBool(val);
53663         }
53664         return Roo.util.Format.htmlEncode(rv);
53665     },
53666
53667     getPropertyName : function(name){
53668         var pn = this.grid.propertyNames;
53669         return pn && pn[name] ? pn[name] : name;
53670     },
53671
53672     getCellEditor : function(colIndex, rowIndex){
53673         var p = this.store.getProperty(rowIndex);
53674         var n = p.data['name'], val = p.data['value'];
53675         
53676         if(typeof(this.grid.customEditors[n]) == 'string'){
53677             return this.editors[this.grid.customEditors[n]];
53678         }
53679         if(typeof(this.grid.customEditors[n]) != 'undefined'){
53680             return this.grid.customEditors[n];
53681         }
53682         if(val instanceof Date){
53683             return this.editors['date'];
53684         }else if(typeof val == 'number'){
53685             return this.editors['number'];
53686         }else if(typeof val == 'boolean'){
53687             return this.editors['boolean'];
53688         }else{
53689             return this.editors['string'];
53690         }
53691     }
53692 });
53693
53694 /**
53695  * @class Roo.grid.PropertyGrid
53696  * @extends Roo.grid.EditorGrid
53697  * This class represents the  interface of a component based property grid control.
53698  * <br><br>Usage:<pre><code>
53699  var grid = new Roo.grid.PropertyGrid("my-container-id", {
53700       
53701  });
53702  // set any options
53703  grid.render();
53704  * </code></pre>
53705   
53706  * @constructor
53707  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
53708  * The container MUST have some type of size defined for the grid to fill. The container will be
53709  * automatically set to position relative if it isn't already.
53710  * @param {Object} config A config object that sets properties on this grid.
53711  */
53712 Roo.grid.PropertyGrid = function(container, config){
53713     config = config || {};
53714     var store = new Roo.grid.PropertyStore(this);
53715     this.store = store;
53716     var cm = new Roo.grid.PropertyColumnModel(this, store);
53717     store.store.sort('name', 'ASC');
53718     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
53719         ds: store.store,
53720         cm: cm,
53721         enableColLock:false,
53722         enableColumnMove:false,
53723         stripeRows:false,
53724         trackMouseOver: false,
53725         clicksToEdit:1
53726     }, config));
53727     this.getGridEl().addClass('x-props-grid');
53728     this.lastEditRow = null;
53729     this.on('columnresize', this.onColumnResize, this);
53730     this.addEvents({
53731          /**
53732              * @event beforepropertychange
53733              * Fires before a property changes (return false to stop?)
53734              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
53735              * @param {String} id Record Id
53736              * @param {String} newval New Value
53737          * @param {String} oldval Old Value
53738              */
53739         "beforepropertychange": true,
53740         /**
53741              * @event propertychange
53742              * Fires after a property changes
53743              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
53744              * @param {String} id Record Id
53745              * @param {String} newval New Value
53746          * @param {String} oldval Old Value
53747              */
53748         "propertychange": true
53749     });
53750     this.customEditors = this.customEditors || {};
53751 };
53752 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
53753     
53754      /**
53755      * @cfg {Object} customEditors map of colnames=> custom editors.
53756      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
53757      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
53758      * false disables editing of the field.
53759          */
53760     
53761       /**
53762      * @cfg {Object} propertyNames map of property Names to their displayed value
53763          */
53764     
53765     render : function(){
53766         Roo.grid.PropertyGrid.superclass.render.call(this);
53767         this.autoSize.defer(100, this);
53768     },
53769
53770     autoSize : function(){
53771         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
53772         if(this.view){
53773             this.view.fitColumns();
53774         }
53775     },
53776
53777     onColumnResize : function(){
53778         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
53779         this.autoSize();
53780     },
53781     /**
53782      * Sets the data for the Grid
53783      * accepts a Key => Value object of all the elements avaiable.
53784      * @param {Object} data  to appear in grid.
53785      */
53786     setSource : function(source){
53787         this.store.setSource(source);
53788         //this.autoSize();
53789     },
53790     /**
53791      * Gets all the data from the grid.
53792      * @return {Object} data  data stored in grid
53793      */
53794     getSource : function(){
53795         return this.store.getSource();
53796     }
53797 });/*
53798  * Based on:
53799  * Ext JS Library 1.1.1
53800  * Copyright(c) 2006-2007, Ext JS, LLC.
53801  *
53802  * Originally Released Under LGPL - original licence link has changed is not relivant.
53803  *
53804  * Fork - LGPL
53805  * <script type="text/javascript">
53806  */
53807  
53808 /**
53809  * @class Roo.LoadMask
53810  * A simple utility class for generically masking elements while loading data.  If the element being masked has
53811  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
53812  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
53813  * element's UpdateManager load indicator and will be destroyed after the initial load.
53814  * @constructor
53815  * Create a new LoadMask
53816  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
53817  * @param {Object} config The config object
53818  */
53819 Roo.LoadMask = function(el, config){
53820     this.el = Roo.get(el);
53821     Roo.apply(this, config);
53822     if(this.store){
53823         this.store.on('beforeload', this.onBeforeLoad, this);
53824         this.store.on('load', this.onLoad, this);
53825         this.store.on('loadexception', this.onLoadException, this);
53826         this.removeMask = false;
53827     }else{
53828         var um = this.el.getUpdateManager();
53829         um.showLoadIndicator = false; // disable the default indicator
53830         um.on('beforeupdate', this.onBeforeLoad, this);
53831         um.on('update', this.onLoad, this);
53832         um.on('failure', this.onLoad, this);
53833         this.removeMask = true;
53834     }
53835 };
53836
53837 Roo.LoadMask.prototype = {
53838     /**
53839      * @cfg {Boolean} removeMask
53840      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
53841      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
53842      */
53843     /**
53844      * @cfg {String} msg
53845      * The text to display in a centered loading message box (defaults to 'Loading...')
53846      */
53847     msg : 'Loading...',
53848     /**
53849      * @cfg {String} msgCls
53850      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
53851      */
53852     msgCls : 'x-mask-loading',
53853
53854     /**
53855      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
53856      * @type Boolean
53857      */
53858     disabled: false,
53859
53860     /**
53861      * Disables the mask to prevent it from being displayed
53862      */
53863     disable : function(){
53864        this.disabled = true;
53865     },
53866
53867     /**
53868      * Enables the mask so that it can be displayed
53869      */
53870     enable : function(){
53871         this.disabled = false;
53872     },
53873     
53874     onLoadException : function()
53875     {
53876         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53877             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53878         }
53879         this.el.unmask(this.removeMask);
53880     },
53881     // private
53882     onLoad : function()
53883     {
53884         this.el.unmask(this.removeMask);
53885     },
53886
53887     // private
53888     onBeforeLoad : function(){
53889         if(!this.disabled){
53890             this.el.mask(this.msg, this.msgCls);
53891         }
53892     },
53893
53894     // private
53895     destroy : function(){
53896         if(this.store){
53897             this.store.un('beforeload', this.onBeforeLoad, this);
53898             this.store.un('load', this.onLoad, this);
53899             this.store.un('loadexception', this.onLoadException, this);
53900         }else{
53901             var um = this.el.getUpdateManager();
53902             um.un('beforeupdate', this.onBeforeLoad, this);
53903             um.un('update', this.onLoad, this);
53904             um.un('failure', this.onLoad, this);
53905         }
53906     }
53907 };/*
53908  * Based on:
53909  * Ext JS Library 1.1.1
53910  * Copyright(c) 2006-2007, Ext JS, LLC.
53911  *
53912  * Originally Released Under LGPL - original licence link has changed is not relivant.
53913  *
53914  * Fork - LGPL
53915  * <script type="text/javascript">
53916  */
53917
53918
53919 /**
53920  * @class Roo.XTemplate
53921  * @extends Roo.Template
53922  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
53923 <pre><code>
53924 var t = new Roo.XTemplate(
53925         '&lt;select name="{name}"&gt;',
53926                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
53927         '&lt;/select&gt;'
53928 );
53929  
53930 // then append, applying the master template values
53931  </code></pre>
53932  *
53933  * Supported features:
53934  *
53935  *  Tags:
53936
53937 <pre><code>
53938       {a_variable} - output encoded.
53939       {a_variable.format:("Y-m-d")} - call a method on the variable
53940       {a_variable:raw} - unencoded output
53941       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
53942       {a_variable:this.method_on_template(...)} - call a method on the template object.
53943  
53944 </code></pre>
53945  *  The tpl tag:
53946 <pre><code>
53947         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
53948         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
53949         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
53950         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
53951   
53952         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
53953         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
53954 </code></pre>
53955  *      
53956  */
53957 Roo.XTemplate = function()
53958 {
53959     Roo.XTemplate.superclass.constructor.apply(this, arguments);
53960     if (this.html) {
53961         this.compile();
53962     }
53963 };
53964
53965
53966 Roo.extend(Roo.XTemplate, Roo.Template, {
53967
53968     /**
53969      * The various sub templates
53970      */
53971     tpls : false,
53972     /**
53973      *
53974      * basic tag replacing syntax
53975      * WORD:WORD()
53976      *
53977      * // you can fake an object call by doing this
53978      *  x.t:(test,tesT) 
53979      * 
53980      */
53981     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
53982
53983     /**
53984      * compile the template
53985      *
53986      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
53987      *
53988      */
53989     compile: function()
53990     {
53991         var s = this.html;
53992      
53993         s = ['<tpl>', s, '</tpl>'].join('');
53994     
53995         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
53996             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
53997             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
53998             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
53999             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
54000             m,
54001             id     = 0,
54002             tpls   = [];
54003     
54004         while(true == !!(m = s.match(re))){
54005             var forMatch   = m[0].match(nameRe),
54006                 ifMatch   = m[0].match(ifRe),
54007                 execMatch   = m[0].match(execRe),
54008                 namedMatch   = m[0].match(namedRe),
54009                 
54010                 exp  = null, 
54011                 fn   = null,
54012                 exec = null,
54013                 name = forMatch && forMatch[1] ? forMatch[1] : '';
54014                 
54015             if (ifMatch) {
54016                 // if - puts fn into test..
54017                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
54018                 if(exp){
54019                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
54020                 }
54021             }
54022             
54023             if (execMatch) {
54024                 // exec - calls a function... returns empty if true is  returned.
54025                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
54026                 if(exp){
54027                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
54028                 }
54029             }
54030             
54031             
54032             if (name) {
54033                 // for = 
54034                 switch(name){
54035                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
54036                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
54037                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
54038                 }
54039             }
54040             var uid = namedMatch ? namedMatch[1] : id;
54041             
54042             
54043             tpls.push({
54044                 id:     namedMatch ? namedMatch[1] : id,
54045                 target: name,
54046                 exec:   exec,
54047                 test:   fn,
54048                 body:   m[1] || ''
54049             });
54050             if (namedMatch) {
54051                 s = s.replace(m[0], '');
54052             } else { 
54053                 s = s.replace(m[0], '{xtpl'+ id + '}');
54054             }
54055             ++id;
54056         }
54057         this.tpls = [];
54058         for(var i = tpls.length-1; i >= 0; --i){
54059             this.compileTpl(tpls[i]);
54060             this.tpls[tpls[i].id] = tpls[i];
54061         }
54062         this.master = tpls[tpls.length-1];
54063         return this;
54064     },
54065     /**
54066      * same as applyTemplate, except it's done to one of the subTemplates
54067      * when using named templates, you can do:
54068      *
54069      * var str = pl.applySubTemplate('your-name', values);
54070      *
54071      * 
54072      * @param {Number} id of the template
54073      * @param {Object} values to apply to template
54074      * @param {Object} parent (normaly the instance of this object)
54075      */
54076     applySubTemplate : function(id, values, parent)
54077     {
54078         
54079         
54080         var t = this.tpls[id];
54081         
54082         
54083         try { 
54084             if(t.test && !t.test.call(this, values, parent)){
54085                 return '';
54086             }
54087         } catch(e) {
54088             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
54089             Roo.log(e.toString());
54090             Roo.log(t.test);
54091             return ''
54092         }
54093         try { 
54094             
54095             if(t.exec && t.exec.call(this, values, parent)){
54096                 return '';
54097             }
54098         } catch(e) {
54099             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
54100             Roo.log(e.toString());
54101             Roo.log(t.exec);
54102             return ''
54103         }
54104         try {
54105             var vs = t.target ? t.target.call(this, values, parent) : values;
54106             parent = t.target ? values : parent;
54107             if(t.target && vs instanceof Array){
54108                 var buf = [];
54109                 for(var i = 0, len = vs.length; i < len; i++){
54110                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
54111                 }
54112                 return buf.join('');
54113             }
54114             return t.compiled.call(this, vs, parent);
54115         } catch (e) {
54116             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
54117             Roo.log(e.toString());
54118             Roo.log(t.compiled);
54119             return '';
54120         }
54121     },
54122
54123     compileTpl : function(tpl)
54124     {
54125         var fm = Roo.util.Format;
54126         var useF = this.disableFormats !== true;
54127         var sep = Roo.isGecko ? "+" : ",";
54128         var undef = function(str) {
54129             Roo.log("Property not found :"  + str);
54130             return '';
54131         };
54132         
54133         var fn = function(m, name, format, args)
54134         {
54135             //Roo.log(arguments);
54136             args = args ? args.replace(/\\'/g,"'") : args;
54137             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
54138             if (typeof(format) == 'undefined') {
54139                 format= 'htmlEncode';
54140             }
54141             if (format == 'raw' ) {
54142                 format = false;
54143             }
54144             
54145             if(name.substr(0, 4) == 'xtpl'){
54146                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
54147             }
54148             
54149             // build an array of options to determine if value is undefined..
54150             
54151             // basically get 'xxxx.yyyy' then do
54152             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
54153             //    (function () { Roo.log("Property not found"); return ''; })() :
54154             //    ......
54155             
54156             var udef_ar = [];
54157             var lookfor = '';
54158             Roo.each(name.split('.'), function(st) {
54159                 lookfor += (lookfor.length ? '.': '') + st;
54160                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
54161             });
54162             
54163             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
54164             
54165             
54166             if(format && useF){
54167                 
54168                 args = args ? ',' + args : "";
54169                  
54170                 if(format.substr(0, 5) != "this."){
54171                     format = "fm." + format + '(';
54172                 }else{
54173                     format = 'this.call("'+ format.substr(5) + '", ';
54174                     args = ", values";
54175                 }
54176                 
54177                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
54178             }
54179              
54180             if (args.length) {
54181                 // called with xxyx.yuu:(test,test)
54182                 // change to ()
54183                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
54184             }
54185             // raw.. - :raw modifier..
54186             return "'"+ sep + udef_st  + name + ")"+sep+"'";
54187             
54188         };
54189         var body;
54190         // branched to use + in gecko and [].join() in others
54191         if(Roo.isGecko){
54192             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
54193                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
54194                     "';};};";
54195         }else{
54196             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
54197             body.push(tpl.body.replace(/(\r\n|\n)/g,
54198                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
54199             body.push("'].join('');};};");
54200             body = body.join('');
54201         }
54202         
54203         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
54204        
54205         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
54206         eval(body);
54207         
54208         return this;
54209     },
54210
54211     applyTemplate : function(values){
54212         return this.master.compiled.call(this, values, {});
54213         //var s = this.subs;
54214     },
54215
54216     apply : function(){
54217         return this.applyTemplate.apply(this, arguments);
54218     }
54219
54220  });
54221
54222 Roo.XTemplate.from = function(el){
54223     el = Roo.getDom(el);
54224     return new Roo.XTemplate(el.value || el.innerHTML);
54225 };/*
54226  * Original code for Roojs - LGPL
54227  * <script type="text/javascript">
54228  */
54229  
54230 /**
54231  * @class Roo.XComponent
54232  * A delayed Element creator...
54233  * Or a way to group chunks of interface together.
54234  * 
54235  * Mypart.xyx = new Roo.XComponent({
54236
54237     parent : 'Mypart.xyz', // empty == document.element.!!
54238     order : '001',
54239     name : 'xxxx'
54240     region : 'xxxx'
54241     disabled : function() {} 
54242      
54243     tree : function() { // return an tree of xtype declared components
54244         var MODULE = this;
54245         return 
54246         {
54247             xtype : 'NestedLayoutPanel',
54248             // technicall
54249         }
54250      ]
54251  *})
54252  *
54253  *
54254  * It can be used to build a big heiracy, with parent etc.
54255  * or you can just use this to render a single compoent to a dom element
54256  * MYPART.render(Roo.Element | String(id) | dom_element )
54257  * 
54258  * @extends Roo.util.Observable
54259  * @constructor
54260  * @param cfg {Object} configuration of component
54261  * 
54262  */
54263 Roo.XComponent = function(cfg) {
54264     Roo.apply(this, cfg);
54265     this.addEvents({ 
54266         /**
54267              * @event built
54268              * Fires when this the componnt is built
54269              * @param {Roo.XComponent} c the component
54270              */
54271         'built' : true
54272         
54273     });
54274     this.region = this.region || 'center'; // default..
54275     Roo.XComponent.register(this);
54276     this.modules = false;
54277     this.el = false; // where the layout goes..
54278     
54279     
54280 }
54281 Roo.extend(Roo.XComponent, Roo.util.Observable, {
54282     /**
54283      * @property el
54284      * The created element (with Roo.factory())
54285      * @type {Roo.Layout}
54286      */
54287     el  : false,
54288     
54289     /**
54290      * @property el
54291      * for BC  - use el in new code
54292      * @type {Roo.Layout}
54293      */
54294     panel : false,
54295     
54296     /**
54297      * @property layout
54298      * for BC  - use el in new code
54299      * @type {Roo.Layout}
54300      */
54301     layout : false,
54302     
54303      /**
54304      * @cfg {Function|boolean} disabled
54305      * If this module is disabled by some rule, return true from the funtion
54306      */
54307     disabled : false,
54308     
54309     /**
54310      * @cfg {String} parent 
54311      * Name of parent element which it get xtype added to..
54312      */
54313     parent: false,
54314     
54315     /**
54316      * @cfg {String} order
54317      * Used to set the order in which elements are created (usefull for multiple tabs)
54318      */
54319     
54320     order : false,
54321     /**
54322      * @cfg {String} name
54323      * String to display while loading.
54324      */
54325     name : false,
54326     /**
54327      * @cfg {String} region
54328      * Region to render component to (defaults to center)
54329      */
54330     region : 'center',
54331     
54332     /**
54333      * @cfg {Array} items
54334      * A single item array - the first element is the root of the tree..
54335      * It's done this way to stay compatible with the Xtype system...
54336      */
54337     items : false,
54338     
54339     /**
54340      * @property _tree
54341      * The method that retuns the tree of parts that make up this compoennt 
54342      * @type {function}
54343      */
54344     _tree  : false,
54345     
54346      /**
54347      * render
54348      * render element to dom or tree
54349      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
54350      */
54351     
54352     render : function(el)
54353     {
54354         
54355         el = el || false;
54356         var hp = this.parent ? 1 : 0;
54357         
54358         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
54359             // if parent is a '#.....' string, then let's use that..
54360             var ename = this.parent.substr(1)
54361             this.parent = false;
54362             el = Roo.get(ename);
54363             if (!el) {
54364                 Roo.log("Warning - element can not be found :#" + ename );
54365                 return;
54366             }
54367         }
54368         
54369         
54370         if (!this.parent) {
54371             
54372             el = el ? Roo.get(el) : false;      
54373             
54374             // it's a top level one..
54375             this.parent =  {
54376                 el : new Roo.BorderLayout(el || document.body, {
54377                 
54378                      center: {
54379                          titlebar: false,
54380                          autoScroll:false,
54381                          closeOnTab: true,
54382                          tabPosition: 'top',
54383                           //resizeTabs: true,
54384                          alwaysShowTabs: el && hp? false :  true,
54385                          hideTabs: el || !hp ? true :  false,
54386                          minTabWidth: 140
54387                      }
54388                  })
54389             }
54390         }
54391         
54392                 if (!this.parent.el) {
54393                         // probably an old style ctor, which has been disabled.
54394                         return;
54395                         
54396                 }
54397                 // The 'tree' method is  '_tree now' 
54398             
54399         var tree = this._tree ? this._tree() : this.tree();
54400         tree.region = tree.region || this.region;
54401         this.el = this.parent.el.addxtype(tree);
54402         this.fireEvent('built', this);
54403         
54404         this.panel = this.el;
54405         this.layout = this.panel.layout;
54406                 this.parentLayout = this.parent.layout  || false;  
54407          
54408     }
54409     
54410 });
54411
54412 Roo.apply(Roo.XComponent, {
54413     /**
54414      * @property  hideProgress
54415      * true to disable the building progress bar.. usefull on single page renders.
54416      * @type Boolean
54417      */
54418     hideProgress : false,
54419     /**
54420      * @property  buildCompleted
54421      * True when the builder has completed building the interface.
54422      * @type Boolean
54423      */
54424     buildCompleted : false,
54425      
54426     /**
54427      * @property  topModule
54428      * the upper most module - uses document.element as it's constructor.
54429      * @type Object
54430      */
54431      
54432     topModule  : false,
54433       
54434     /**
54435      * @property  modules
54436      * array of modules to be created by registration system.
54437      * @type {Array} of Roo.XComponent
54438      */
54439     
54440     modules : [],
54441     /**
54442      * @property  elmodules
54443      * array of modules to be created by which use #ID 
54444      * @type {Array} of Roo.XComponent
54445      */
54446      
54447     elmodules : [],
54448
54449     
54450     /**
54451      * Register components to be built later.
54452      *
54453      * This solves the following issues
54454      * - Building is not done on page load, but after an authentication process has occured.
54455      * - Interface elements are registered on page load
54456      * - Parent Interface elements may not be loaded before child, so this handles that..
54457      * 
54458      *
54459      * example:
54460      * 
54461      * MyApp.register({
54462           order : '000001',
54463           module : 'Pman.Tab.projectMgr',
54464           region : 'center',
54465           parent : 'Pman.layout',
54466           disabled : false,  // or use a function..
54467         })
54468      
54469      * * @param {Object} details about module
54470      */
54471     register : function(obj) {
54472                 
54473         Roo.XComponent.event.fireEvent('register', obj);
54474         switch(typeof(obj.disabled) ) {
54475                 
54476             case 'undefined':
54477                 break;
54478             
54479             case 'function':
54480                 if ( obj.disabled() ) {
54481                         return;
54482                 }
54483                 break;
54484             
54485             default:
54486                 if (obj.disabled) {
54487                         return;
54488                 }
54489                 break;
54490         }
54491                 
54492         this.modules.push(obj);
54493          
54494     },
54495     /**
54496      * convert a string to an object..
54497      * eg. 'AAA.BBB' -> finds AAA.BBB
54498
54499      */
54500     
54501     toObject : function(str)
54502     {
54503         if (!str || typeof(str) == 'object') {
54504             return str;
54505         }
54506         if (str.substring(0,1) == '#') {
54507             return str;
54508         }
54509
54510         var ar = str.split('.');
54511         var rt, o;
54512         rt = ar.shift();
54513             /** eval:var:o */
54514         try {
54515             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
54516         } catch (e) {
54517             throw "Module not found : " + str;
54518         }
54519         
54520         if (o === false) {
54521             throw "Module not found : " + str;
54522         }
54523         Roo.each(ar, function(e) {
54524             if (typeof(o[e]) == 'undefined') {
54525                 throw "Module not found : " + str;
54526             }
54527             o = o[e];
54528         });
54529         
54530         return o;
54531         
54532     },
54533     
54534     
54535     /**
54536      * move modules into their correct place in the tree..
54537      * 
54538      */
54539     preBuild : function ()
54540     {
54541         var _t = this;
54542         Roo.each(this.modules , function (obj)
54543         {
54544             Roo.XComponent.event.fireEvent('beforebuild', obj);
54545             
54546             var opar = obj.parent;
54547             try { 
54548                 obj.parent = this.toObject(opar);
54549             } catch(e) {
54550                 Roo.log("parent:toObject failed: " + e.toString());
54551                 return;
54552             }
54553             
54554             if (!obj.parent) {
54555                 Roo.debug && Roo.log("GOT top level module");
54556                 Roo.debug && Roo.log(obj);
54557                 obj.modules = new Roo.util.MixedCollection(false, 
54558                     function(o) { return o.order + '' }
54559                 );
54560                 this.topModule = obj;
54561                 return;
54562             }
54563                         // parent is a string (usually a dom element name..)
54564             if (typeof(obj.parent) == 'string') {
54565                 this.elmodules.push(obj);
54566                 return;
54567             }
54568             if (obj.parent.constructor != Roo.XComponent) {
54569                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
54570             }
54571             if (!obj.parent.modules) {
54572                 obj.parent.modules = new Roo.util.MixedCollection(false, 
54573                     function(o) { return o.order + '' }
54574                 );
54575             }
54576             if (obj.parent.disabled) {
54577                 obj.disabled = true;
54578             }
54579             obj.parent.modules.add(obj);
54580         }, this);
54581     },
54582     
54583      /**
54584      * make a list of modules to build.
54585      * @return {Array} list of modules. 
54586      */ 
54587     
54588     buildOrder : function()
54589     {
54590         var _this = this;
54591         var cmp = function(a,b) {   
54592             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
54593         };
54594         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
54595             throw "No top level modules to build";
54596         }
54597         
54598         // make a flat list in order of modules to build.
54599         var mods = this.topModule ? [ this.topModule ] : [];
54600                 
54601         // elmodules (is a list of DOM based modules )
54602         Roo.each(this.elmodules, function(e) {
54603             mods.push(e)
54604         });
54605
54606         
54607         // add modules to their parents..
54608         var addMod = function(m) {
54609             Roo.debug && Roo.log("build Order: add: " + m.name);
54610             
54611         mods.push(m);
54612         if (m.modules && !m.disabled) {
54613             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
54614             m.modules.keySort('ASC',  cmp );
54615             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
54616
54617             m.modules.each(addMod);
54618         } else {
54619             Roo.debug && Roo.log("build Order: no child modules");
54620             }
54621             // not sure if this is used any more..
54622             if (m.finalize) {
54623                 m.finalize.name = m.name + " (clean up) ";
54624                 mods.push(m.finalize);
54625             }
54626             
54627         }
54628         if (this.topModule) { 
54629             this.topModule.modules.keySort('ASC',  cmp );
54630             this.topModule.modules.each(addMod);
54631         }
54632         return mods;
54633     },
54634     
54635      /**
54636      * Build the registered modules.
54637      * @param {Object} parent element.
54638      * @param {Function} optional method to call after module has been added.
54639      * 
54640      */ 
54641    
54642     build : function() 
54643     {
54644         
54645         this.preBuild();
54646         var mods = this.buildOrder();
54647       
54648         //this.allmods = mods;
54649         //Roo.debug && Roo.log(mods);
54650         //return;
54651         if (!mods.length) { // should not happen
54652             throw "NO modules!!!";
54653         }
54654         
54655         
54656         var msg = "Building Interface...";
54657         // flash it up as modal - so we store the mask!?
54658         if (!this.hideProgress) {
54659             Roo.MessageBox.show({ title: 'loading' });
54660             Roo.MessageBox.show({
54661                title: "Please wait...",
54662                msg: msg,
54663                width:450,
54664                progress:true,
54665                closable:false,
54666                modal: false
54667               
54668             });
54669         }
54670         var total = mods.length;
54671         
54672         var _this = this;
54673         var progressRun = function() {
54674             if (!mods.length) {
54675                 Roo.debug && Roo.log('hide?');
54676                 if (!this.hideProgress) {
54677                     Roo.MessageBox.hide();
54678                 }
54679                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
54680                 
54681                 // THE END...
54682                 return false;   
54683             }
54684             
54685             var m = mods.shift();
54686             
54687             
54688             Roo.debug && Roo.log(m);
54689             // not sure if this is supported any more.. - modules that are are just function
54690             if (typeof(m) == 'function') { 
54691                 m.call(this);
54692                 return progressRun.defer(10, _this);
54693             } 
54694             
54695             
54696             msg = "Building Interface " + (total  - mods.length) + 
54697                     " of " + total + 
54698                     (m.name ? (' - ' + m.name) : '');
54699                         Roo.debug && Roo.log(msg);
54700             if (!this.hideProgress) { 
54701                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
54702             }
54703             
54704          
54705             // is the module disabled?
54706             var disabled = (typeof(m.disabled) == 'function') ?
54707                 m.disabled.call(m.module.disabled) : m.disabled;    
54708             
54709             
54710             if (disabled) {
54711                 return progressRun(); // we do not update the display!
54712             }
54713             
54714             // now build 
54715             
54716                         
54717                         
54718             m.render();
54719             // it's 10 on top level, and 1 on others??? why...
54720             return progressRun.defer(10, _this);
54721              
54722         }
54723         progressRun.defer(1, _this);
54724      
54725         
54726         
54727     },
54728         
54729         
54730         /**
54731          * Event Object.
54732          *
54733          *
54734          */
54735         event: false, 
54736     /**
54737          * wrapper for event.on - aliased later..  
54738          * Typically use to register a event handler for register:
54739          *
54740          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
54741          *
54742          */
54743     on : false
54744    
54745     
54746     
54747 });
54748
54749 Roo.XComponent.event = new Roo.util.Observable({
54750                 events : { 
54751                         /**
54752                          * @event register
54753                          * Fires when an Component is registered,
54754                          * set the disable property on the Component to stop registration.
54755                          * @param {Roo.XComponent} c the component being registerd.
54756                          * 
54757                          */
54758                         'register' : true,
54759             /**
54760                          * @event beforebuild
54761                          * Fires before each Component is built
54762                          * can be used to apply permissions.
54763                          * @param {Roo.XComponent} c the component being registerd.
54764                          * 
54765                          */
54766                         'beforebuild' : true,
54767                         /**
54768                          * @event buildcomplete
54769                          * Fires on the top level element when all elements have been built
54770                          * @param {Roo.XComponent} the top level component.
54771                          */
54772                         'buildcomplete' : true
54773                         
54774                 }
54775 });
54776
54777 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
54778  //<script type="text/javascript">
54779
54780
54781 /**
54782  * @class Roo.Login
54783  * @extends Roo.LayoutDialog
54784  * A generic Login Dialog..... - only one needed in theory!?!?
54785  *
54786  * Fires XComponent builder on success...
54787  * 
54788  * Sends 
54789  *    username,password, lang = for login actions.
54790  *    check = 1 for periodic checking that sesion is valid.
54791  *    passwordRequest = email request password
54792  *    logout = 1 = to logout
54793  * 
54794  * Affects: (this id="????" elements)
54795  *   loading  (removed) (used to indicate application is loading)
54796  *   loading-mask (hides) (used to hide application when it's building loading)
54797  *   
54798  * 
54799  * Usage: 
54800  *    
54801  * 
54802  * Myapp.login = Roo.Login({
54803      url: xxxx,
54804    
54805      realm : 'Myapp', 
54806      
54807      
54808      method : 'POST',
54809      
54810      
54811      * 
54812  })
54813  * 
54814  * 
54815  * 
54816  **/
54817  
54818 Roo.Login = function(cfg)
54819 {
54820     this.addEvents({
54821         'refreshed' : true
54822     });
54823     
54824     Roo.apply(this,cfg);
54825     
54826     Roo.onReady(function() {
54827         this.onLoad();
54828     }, this);
54829     // call parent..
54830     
54831    
54832     Roo.Login.superclass.constructor.call(this, this);
54833     //this.addxtype(this.items[0]);
54834     
54835     
54836 }
54837
54838
54839 Roo.extend(Roo.Login, Roo.LayoutDialog, {
54840     
54841     /**
54842      * @cfg {String} method
54843      * Method used to query for login details.
54844      */
54845     
54846     method : 'POST',
54847     /**
54848      * @cfg {String} url
54849      * URL to query login data. - eg. baseURL + '/Login.php'
54850      */
54851     url : '',
54852     
54853     /**
54854      * @property user
54855      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
54856      * @type {Object} 
54857      */
54858     user : false,
54859     /**
54860      * @property checkFails
54861      * Number of times we have attempted to get authentication check, and failed.
54862      * @type {Number} 
54863      */
54864     checkFails : 0,
54865       /**
54866      * @property intervalID
54867      * The window interval that does the constant login checking.
54868      * @type {Number} 
54869      */
54870     intervalID : 0,
54871     
54872     
54873     onLoad : function() // called on page load...
54874     {
54875         // load 
54876          
54877         if (Roo.get('loading')) { // clear any loading indicator..
54878             Roo.get('loading').remove();
54879         }
54880         
54881         //this.switchLang('en'); // set the language to english..
54882        
54883         this.check({
54884             success:  function(response, opts)  {  // check successfull...
54885             
54886                 var res = this.processResponse(response);
54887                 this.checkFails =0;
54888                 if (!res.success) { // error!
54889                     this.checkFails = 5;
54890                     //console.log('call failure');
54891                     return this.failure(response,opts);
54892                 }
54893                 
54894                 if (!res.data.id) { // id=0 == login failure.
54895                     return this.show();
54896                 }
54897                 
54898                               
54899                         //console.log(success);
54900                 this.fillAuth(res.data);   
54901                 this.checkFails =0;
54902                 Roo.XComponent.build();
54903             },
54904             failure : this.show
54905         });
54906         
54907     }, 
54908     
54909     
54910     check: function(cfg) // called every so often to refresh cookie etc..
54911     {
54912         if (cfg.again) { // could be undefined..
54913             this.checkFails++;
54914         } else {
54915             this.checkFails = 0;
54916         }
54917         var _this = this;
54918         if (this.sending) {
54919             if ( this.checkFails > 4) {
54920                 Roo.MessageBox.alert("Error",  
54921                     "Error getting authentication status. - try reloading, or wait a while", function() {
54922                         _this.sending = false;
54923                     }); 
54924                 return;
54925             }
54926             cfg.again = true;
54927             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
54928             return;
54929         }
54930         this.sending = true;
54931         
54932         Roo.Ajax.request({  
54933             url: this.url,
54934             params: {
54935                 getAuthUser: true
54936             },  
54937             method: this.method,
54938             success:  cfg.success || this.success,
54939             failure : cfg.failure || this.failure,
54940             scope : this,
54941             callCfg : cfg
54942               
54943         });  
54944     }, 
54945     
54946     
54947     logout: function()
54948     {
54949         window.onbeforeunload = function() { }; // false does not work for IE..
54950         this.user = false;
54951         var _this = this;
54952         
54953         Roo.Ajax.request({  
54954             url: this.url,
54955             params: {
54956                 logout: 1
54957             },  
54958             method: 'GET',
54959             failure : function() {
54960                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
54961                     document.location = document.location.toString() + '?ts=' + Math.random();
54962                 });
54963                 
54964             },
54965             success : function() {
54966                 _this.user = false;
54967                 this.checkFails =0;
54968                 // fixme..
54969                 document.location = document.location.toString() + '?ts=' + Math.random();
54970             }
54971               
54972               
54973         }); 
54974     },
54975     
54976     processResponse : function (response)
54977     {
54978         var res = '';
54979         try {
54980             res = Roo.decode(response.responseText);
54981             // oops...
54982             if (typeof(res) != 'object') {
54983                 res = { success : false, errorMsg : res, errors : true };
54984             }
54985             if (typeof(res.success) == 'undefined') {
54986                 res.success = false;
54987             }
54988             
54989         } catch(e) {
54990             res = { success : false,  errorMsg : response.responseText, errors : true };
54991         }
54992         return res;
54993     },
54994     
54995     success : function(response, opts)  // check successfull...
54996     {  
54997         this.sending = false;
54998         var res = this.processResponse(response);
54999         if (!res.success) {
55000             return this.failure(response, opts);
55001         }
55002         if (!res.data || !res.data.id) {
55003             return this.failure(response,opts);
55004         }
55005         //console.log(res);
55006         this.fillAuth(res.data);
55007         
55008         this.checkFails =0;
55009         
55010     },
55011     
55012     
55013     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
55014     {
55015         this.authUser = -1;
55016         this.sending = false;
55017         var res = this.processResponse(response);
55018         //console.log(res);
55019         if ( this.checkFails > 2) {
55020         
55021             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
55022                 "Error getting authentication status. - try reloading"); 
55023             return;
55024         }
55025         opts.callCfg.again = true;
55026         this.check.defer(1000, this, [ opts.callCfg ]);
55027         return;  
55028     },
55029     
55030     
55031     
55032     fillAuth: function(au) {
55033         this.startAuthCheck();
55034         this.authUserId = au.id;
55035         this.authUser = au;
55036         this.lastChecked = new Date();
55037         this.fireEvent('refreshed', au);
55038         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
55039         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
55040         au.lang = au.lang || 'en';
55041         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
55042         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
55043         this.switchLang(au.lang );
55044         
55045      
55046         // open system... - -on setyp..
55047         if (this.authUserId  < 0) {
55048             Roo.MessageBox.alert("Warning", 
55049                 "This is an open system - please set up a admin user with a password.");  
55050         }
55051          
55052         //Pman.onload(); // which should do nothing if it's a re-auth result...
55053         
55054              
55055     },
55056     
55057     startAuthCheck : function() // starter for timeout checking..
55058     {
55059         if (this.intervalID) { // timer already in place...
55060             return false;
55061         }
55062         var _this = this;
55063         this.intervalID =  window.setInterval(function() {
55064               _this.check(false);
55065             }, 120000); // every 120 secs = 2mins..
55066         
55067         
55068     },
55069          
55070     
55071     switchLang : function (lang) 
55072     {
55073         _T = typeof(_T) == 'undefined' ? false : _T;
55074           if (!_T || !lang.length) {
55075             return;
55076         }
55077         
55078         if (!_T && lang != 'en') {
55079             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
55080             return;
55081         }
55082         
55083         if (typeof(_T.en) == 'undefined') {
55084             _T.en = {};
55085             Roo.apply(_T.en, _T);
55086         }
55087         
55088         if (typeof(_T[lang]) == 'undefined') {
55089             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
55090             return;
55091         }
55092         
55093         
55094         Roo.apply(_T, _T[lang]);
55095         // just need to set the text values for everything...
55096         var _this = this;
55097         /* this will not work ...
55098         if (this.form) { 
55099             
55100                
55101             function formLabel(name, val) {
55102                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
55103             }
55104             
55105             formLabel('password', "Password"+':');
55106             formLabel('username', "Email Address"+':');
55107             formLabel('lang', "Language"+':');
55108             this.dialog.setTitle("Login");
55109             this.dialog.buttons[0].setText("Forgot Password");
55110             this.dialog.buttons[1].setText("Login");
55111         }
55112         */
55113         
55114         
55115     },
55116     
55117     
55118     title: "Login",
55119     modal: true,
55120     width:  350,
55121     //height: 230,
55122     height: 180,
55123     shadow: true,
55124     minWidth:200,
55125     minHeight:180,
55126     //proxyDrag: true,
55127     closable: false,
55128     draggable: false,
55129     collapsible: false,
55130     resizable: false,
55131     center: {  // needed??
55132         autoScroll:false,
55133         titlebar: false,
55134        // tabPosition: 'top',
55135         hideTabs: true,
55136         closeOnTab: true,
55137         alwaysShowTabs: false
55138     } ,
55139     listeners : {
55140         
55141         show  : function(dlg)
55142         {
55143             //console.log(this);
55144             this.form = this.layout.getRegion('center').activePanel.form;
55145             this.form.dialog = dlg;
55146             this.buttons[0].form = this.form;
55147             this.buttons[0].dialog = dlg;
55148             this.buttons[1].form = this.form;
55149             this.buttons[1].dialog = dlg;
55150            
55151            //this.resizeToLogo.defer(1000,this);
55152             // this is all related to resizing for logos..
55153             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
55154            //// if (!sz) {
55155              //   this.resizeToLogo.defer(1000,this);
55156              //   return;
55157            // }
55158             //var w = Ext.lib.Dom.getViewWidth() - 100;
55159             //var h = Ext.lib.Dom.getViewHeight() - 100;
55160             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
55161             //this.center();
55162             if (this.disabled) {
55163                 this.hide();
55164                 return;
55165             }
55166             
55167             if (this.user.id < 0) { // used for inital setup situations.
55168                 return;
55169             }
55170             
55171             if (this.intervalID) {
55172                 // remove the timer
55173                 window.clearInterval(this.intervalID);
55174                 this.intervalID = false;
55175             }
55176             
55177             
55178             if (Roo.get('loading')) {
55179                 Roo.get('loading').remove();
55180             }
55181             if (Roo.get('loading-mask')) {
55182                 Roo.get('loading-mask').hide();
55183             }
55184             
55185             //incomming._node = tnode;
55186             this.form.reset();
55187             //this.dialog.modal = !modal;
55188             //this.dialog.show();
55189             this.el.unmask(); 
55190             
55191             
55192             this.form.setValues({
55193                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
55194                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
55195             });
55196             
55197             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
55198             if (this.form.findField('username').getValue().length > 0 ){
55199                 this.form.findField('password').focus();
55200             } else {
55201                this.form.findField('username').focus();
55202             }
55203     
55204         }
55205     },
55206     items : [
55207          {
55208        
55209             xtype : 'ContentPanel',
55210             xns : Roo,
55211             region: 'center',
55212             fitToFrame : true,
55213             
55214             items : [
55215     
55216                 {
55217                
55218                     xtype : 'Form',
55219                     xns : Roo.form,
55220                     labelWidth: 100,
55221                     style : 'margin: 10px;',
55222                     
55223                     listeners : {
55224                         actionfailed : function(f, act) {
55225                             // form can return { errors: .... }
55226                                 
55227                             //act.result.errors // invalid form element list...
55228                             //act.result.errorMsg// invalid form element list...
55229                             
55230                             this.dialog.el.unmask();
55231                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
55232                                         "Login failed - communication error - try again.");
55233                                       
55234                         },
55235                         actioncomplete: function(re, act) {
55236                              
55237                             Roo.state.Manager.set(
55238                                 this.dialog.realm + '.username',  
55239                                     this.findField('username').getValue()
55240                             );
55241                             Roo.state.Manager.set(
55242                                 this.dialog.realm + '.lang',  
55243                                 this.findField('lang').getValue() 
55244                             );
55245                             
55246                             this.dialog.fillAuth(act.result.data);
55247                               
55248                             this.dialog.hide();
55249                             
55250                             if (Roo.get('loading-mask')) {
55251                                 Roo.get('loading-mask').show();
55252                             }
55253                             Roo.XComponent.build();
55254                             
55255                              
55256                             
55257                         }
55258                     },
55259                     items : [
55260                         {
55261                             xtype : 'TextField',
55262                             xns : Roo.form,
55263                             fieldLabel: "Email Address",
55264                             name: 'username',
55265                             width:200,
55266                             autoCreate : {tag: "input", type: "text", size: "20"}
55267                         },
55268                         {
55269                             xtype : 'TextField',
55270                             xns : Roo.form,
55271                             fieldLabel: "Password",
55272                             inputType: 'password',
55273                             name: 'password',
55274                             width:200,
55275                             autoCreate : {tag: "input", type: "text", size: "20"},
55276                             listeners : {
55277                                 specialkey : function(e,ev) {
55278                                     if (ev.keyCode == 13) {
55279                                         this.form.dialog.el.mask("Logging in");
55280                                         this.form.doAction('submit', {
55281                                             url: this.form.dialog.url,
55282                                             method: this.form.dialog.method
55283                                         });
55284                                     }
55285                                 }
55286                             }  
55287                         },
55288                         {
55289                             xtype : 'ComboBox',
55290                             xns : Roo.form,
55291                             fieldLabel: "Language",
55292                             name : 'langdisp',
55293                             store: {
55294                                 xtype : 'SimpleStore',
55295                                 fields: ['lang', 'ldisp'],
55296                                 data : [
55297                                     [ 'en', 'English' ],
55298                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
55299                                     [ 'zh_CN', '\u7C21\u4E2D' ]
55300                                 ]
55301                             },
55302                             
55303                             valueField : 'lang',
55304                             hiddenName:  'lang',
55305                             width: 200,
55306                             displayField:'ldisp',
55307                             typeAhead: false,
55308                             editable: false,
55309                             mode: 'local',
55310                             triggerAction: 'all',
55311                             emptyText:'Select a Language...',
55312                             selectOnFocus:true,
55313                             listeners : {
55314                                 select :  function(cb, rec, ix) {
55315                                     this.form.switchLang(rec.data.lang);
55316                                 }
55317                             }
55318                         
55319                         }
55320                     ]
55321                 }
55322                   
55323                 
55324             ]
55325         }
55326     ],
55327     buttons : [
55328         {
55329             xtype : 'Button',
55330             xns : 'Roo',
55331             text : "Forgot Password",
55332             listeners : {
55333                 click : function() {
55334                     //console.log(this);
55335                     var n = this.form.findField('username').getValue();
55336                     if (!n.length) {
55337                         Roo.MessageBox.alert("Error", "Fill in your email address");
55338                         return;
55339                     }
55340                     Roo.Ajax.request({
55341                         url: this.dialog.url,
55342                         params: {
55343                             passwordRequest: n
55344                         },
55345                         method: this.dialog.method,
55346                         success:  function(response, opts)  {  // check successfull...
55347                         
55348                             var res = this.dialog.processResponse(response);
55349                             if (!res.success) { // error!
55350                                Roo.MessageBox.alert("Error" ,
55351                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
55352                                return;
55353                             }
55354                             Roo.MessageBox.alert("Notice" ,
55355                                 "Please check you email for the Password Reset message");
55356                         },
55357                         failure : function() {
55358                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
55359                         }
55360                         
55361                     });
55362                 }
55363             }
55364         },
55365         {
55366             xtype : 'Button',
55367             xns : 'Roo',
55368             text : "Login",
55369             listeners : {
55370                 
55371                 click : function () {
55372                         
55373                     this.dialog.el.mask("Logging in");
55374                     this.form.doAction('submit', {
55375                             url: this.dialog.url,
55376                             method: this.dialog.method
55377                     });
55378                 }
55379             }
55380         }
55381     ]
55382   
55383   
55384 })
55385  
55386
55387
55388