Roo/form/ComboBoxArray.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{2,4})"};
1391     
1392     
1393     case "P":
1394         return {g:1,
1395                 c:[
1396                    "o = results[", currentGroup, "];\n",
1397                    "var sn = o.substring(0,1);\n",
1398                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1399                    "var mn = o.substring(4,6) % 60;\n",
1400                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1401                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1402             ].join(""),
1403             s:"([+\-]\\d{4})"};
1404     case "T":
1405         return {g:0,
1406             c:null,
1407             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1408     case "Z":
1409         return {g:1,
1410             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1411                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1412             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1413     default:
1414         return {g:0,
1415             c:null,
1416             s:String.escape(character)};
1417     }
1418 };
1419
1420 /**
1421  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1422  * @return {String} The abbreviated timezone name (e.g. 'CST')
1423  */
1424 Date.prototype.getTimezone = function() {
1425     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1426 };
1427
1428 /**
1429  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1430  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1431  */
1432 Date.prototype.getGMTOffset = function() {
1433     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1434         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1435         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1436 };
1437
1438 /**
1439  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1440  * @return {String} 2-characters representing hours and 2-characters representing minutes
1441  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1442  */
1443 Date.prototype.getGMTColonOffset = function() {
1444         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1445                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1446                 + ":"
1447                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1448 }
1449
1450 /**
1451  * Get the numeric day number of the year, adjusted for leap year.
1452  * @return {Number} 0 through 364 (365 in leap years)
1453  */
1454 Date.prototype.getDayOfYear = function() {
1455     var num = 0;
1456     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1457     for (var i = 0; i < this.getMonth(); ++i) {
1458         num += Date.daysInMonth[i];
1459     }
1460     return num + this.getDate() - 1;
1461 };
1462
1463 /**
1464  * Get the string representation of the numeric week number of the year
1465  * (equivalent to the format specifier 'W').
1466  * @return {String} '00' through '52'
1467  */
1468 Date.prototype.getWeekOfYear = function() {
1469     // Skip to Thursday of this week
1470     var now = this.getDayOfYear() + (4 - this.getDay());
1471     // Find the first Thursday of the year
1472     var jan1 = new Date(this.getFullYear(), 0, 1);
1473     var then = (7 - jan1.getDay() + 4);
1474     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1475 };
1476
1477 /**
1478  * Whether or not the current date is in a leap year.
1479  * @return {Boolean} True if the current date is in a leap year, else false
1480  */
1481 Date.prototype.isLeapYear = function() {
1482     var year = this.getFullYear();
1483     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1484 };
1485
1486 /**
1487  * Get the first day of the current month, adjusted for leap year.  The returned value
1488  * is the numeric day index within the week (0-6) which can be used in conjunction with
1489  * the {@link #monthNames} array to retrieve the textual day name.
1490  * Example:
1491  *<pre><code>
1492 var dt = new Date('1/10/2007');
1493 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1494 </code></pre>
1495  * @return {Number} The day number (0-6)
1496  */
1497 Date.prototype.getFirstDayOfMonth = function() {
1498     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1499     return (day < 0) ? (day + 7) : day;
1500 };
1501
1502 /**
1503  * Get the last day of the current month, adjusted for leap year.  The returned value
1504  * is the numeric day index within the week (0-6) which can be used in conjunction with
1505  * the {@link #monthNames} array to retrieve the textual day name.
1506  * Example:
1507  *<pre><code>
1508 var dt = new Date('1/10/2007');
1509 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1510 </code></pre>
1511  * @return {Number} The day number (0-6)
1512  */
1513 Date.prototype.getLastDayOfMonth = function() {
1514     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1515     return (day < 0) ? (day + 7) : day;
1516 };
1517
1518
1519 /**
1520  * Get the first date of this date's month
1521  * @return {Date}
1522  */
1523 Date.prototype.getFirstDateOfMonth = function() {
1524     return new Date(this.getFullYear(), this.getMonth(), 1);
1525 };
1526
1527 /**
1528  * Get the last date of this date's month
1529  * @return {Date}
1530  */
1531 Date.prototype.getLastDateOfMonth = function() {
1532     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1533 };
1534 /**
1535  * Get the number of days in the current month, adjusted for leap year.
1536  * @return {Number} The number of days in the month
1537  */
1538 Date.prototype.getDaysInMonth = function() {
1539     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1540     return Date.daysInMonth[this.getMonth()];
1541 };
1542
1543 /**
1544  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1545  * @return {String} 'st, 'nd', 'rd' or 'th'
1546  */
1547 Date.prototype.getSuffix = function() {
1548     switch (this.getDate()) {
1549         case 1:
1550         case 21:
1551         case 31:
1552             return "st";
1553         case 2:
1554         case 22:
1555             return "nd";
1556         case 3:
1557         case 23:
1558             return "rd";
1559         default:
1560             return "th";
1561     }
1562 };
1563
1564 // private
1565 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1566
1567 /**
1568  * An array of textual month names.
1569  * Override these values for international dates, for example...
1570  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1571  * @type Array
1572  * @static
1573  */
1574 Date.monthNames =
1575    ["January",
1576     "February",
1577     "March",
1578     "April",
1579     "May",
1580     "June",
1581     "July",
1582     "August",
1583     "September",
1584     "October",
1585     "November",
1586     "December"];
1587
1588 /**
1589  * An array of textual day names.
1590  * Override these values for international dates, for example...
1591  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1592  * @type Array
1593  * @static
1594  */
1595 Date.dayNames =
1596    ["Sunday",
1597     "Monday",
1598     "Tuesday",
1599     "Wednesday",
1600     "Thursday",
1601     "Friday",
1602     "Saturday"];
1603
1604 // private
1605 Date.y2kYear = 50;
1606 // private
1607 Date.monthNumbers = {
1608     Jan:0,
1609     Feb:1,
1610     Mar:2,
1611     Apr:3,
1612     May:4,
1613     Jun:5,
1614     Jul:6,
1615     Aug:7,
1616     Sep:8,
1617     Oct:9,
1618     Nov:10,
1619     Dec:11};
1620
1621 /**
1622  * Creates and returns a new Date instance with the exact same date value as the called instance.
1623  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1624  * variable will also be changed.  When the intention is to create a new variable that will not
1625  * modify the original instance, you should create a clone.
1626  *
1627  * Example of correctly cloning a date:
1628  * <pre><code>
1629 //wrong way:
1630 var orig = new Date('10/1/2006');
1631 var copy = orig;
1632 copy.setDate(5);
1633 document.write(orig);  //returns 'Thu Oct 05 2006'!
1634
1635 //correct way:
1636 var orig = new Date('10/1/2006');
1637 var copy = orig.clone();
1638 copy.setDate(5);
1639 document.write(orig);  //returns 'Thu Oct 01 2006'
1640 </code></pre>
1641  * @return {Date} The new Date instance
1642  */
1643 Date.prototype.clone = function() {
1644         return new Date(this.getTime());
1645 };
1646
1647 /**
1648  * Clears any time information from this date
1649  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1650  @return {Date} this or the clone
1651  */
1652 Date.prototype.clearTime = function(clone){
1653     if(clone){
1654         return this.clone().clearTime();
1655     }
1656     this.setHours(0);
1657     this.setMinutes(0);
1658     this.setSeconds(0);
1659     this.setMilliseconds(0);
1660     return this;
1661 };
1662
1663 // private
1664 // safari setMonth is broken
1665 if(Roo.isSafari){
1666     Date.brokenSetMonth = Date.prototype.setMonth;
1667         Date.prototype.setMonth = function(num){
1668                 if(num <= -1){
1669                         var n = Math.ceil(-num);
1670                         var back_year = Math.ceil(n/12);
1671                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1672                         this.setFullYear(this.getFullYear() - back_year);
1673                         return Date.brokenSetMonth.call(this, month);
1674                 } else {
1675                         return Date.brokenSetMonth.apply(this, arguments);
1676                 }
1677         };
1678 }
1679
1680 /** Date interval constant 
1681 * @static 
1682 * @type String */
1683 Date.MILLI = "ms";
1684 /** Date interval constant 
1685 * @static 
1686 * @type String */
1687 Date.SECOND = "s";
1688 /** Date interval constant 
1689 * @static 
1690 * @type String */
1691 Date.MINUTE = "mi";
1692 /** Date interval constant 
1693 * @static 
1694 * @type String */
1695 Date.HOUR = "h";
1696 /** Date interval constant 
1697 * @static 
1698 * @type String */
1699 Date.DAY = "d";
1700 /** Date interval constant 
1701 * @static 
1702 * @type String */
1703 Date.MONTH = "mo";
1704 /** Date interval constant 
1705 * @static 
1706 * @type String */
1707 Date.YEAR = "y";
1708
1709 /**
1710  * Provides a convenient method of performing basic date arithmetic.  This method
1711  * does not modify the Date instance being called - it creates and returns
1712  * a new Date instance containing the resulting date value.
1713  *
1714  * Examples:
1715  * <pre><code>
1716 //Basic usage:
1717 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1718 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1719
1720 //Negative values will subtract correctly:
1721 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1722 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1723
1724 //You can even chain several calls together in one line!
1725 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1726 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1727  </code></pre>
1728  *
1729  * @param {String} interval   A valid date interval enum value
1730  * @param {Number} value      The amount to add to the current date
1731  * @return {Date} The new Date instance
1732  */
1733 Date.prototype.add = function(interval, value){
1734   var d = this.clone();
1735   if (!interval || value === 0) return d;
1736   switch(interval.toLowerCase()){
1737     case Date.MILLI:
1738       d.setMilliseconds(this.getMilliseconds() + value);
1739       break;
1740     case Date.SECOND:
1741       d.setSeconds(this.getSeconds() + value);
1742       break;
1743     case Date.MINUTE:
1744       d.setMinutes(this.getMinutes() + value);
1745       break;
1746     case Date.HOUR:
1747       d.setHours(this.getHours() + value);
1748       break;
1749     case Date.DAY:
1750       d.setDate(this.getDate() + value);
1751       break;
1752     case Date.MONTH:
1753       var day = this.getDate();
1754       if(day > 28){
1755           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1756       }
1757       d.setDate(day);
1758       d.setMonth(this.getMonth() + value);
1759       break;
1760     case Date.YEAR:
1761       d.setFullYear(this.getFullYear() + value);
1762       break;
1763   }
1764   return d;
1765 };
1766 /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776
1777 /**
1778  * @class Roo.lib.Dom
1779  * @static
1780  * 
1781  * Dom utils (from YIU afaik)
1782  * 
1783  **/
1784 Roo.lib.Dom = {
1785     /**
1786      * Get the view width
1787      * @param {Boolean} full True will get the full document, otherwise it's the view width
1788      * @return {Number} The width
1789      */
1790      
1791     getViewWidth : function(full) {
1792         return full ? this.getDocumentWidth() : this.getViewportWidth();
1793     },
1794     /**
1795      * Get the view height
1796      * @param {Boolean} full True will get the full document, otherwise it's the view height
1797      * @return {Number} The height
1798      */
1799     getViewHeight : function(full) {
1800         return full ? this.getDocumentHeight() : this.getViewportHeight();
1801     },
1802
1803     getDocumentHeight: function() {
1804         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1805         return Math.max(scrollHeight, this.getViewportHeight());
1806     },
1807
1808     getDocumentWidth: function() {
1809         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1810         return Math.max(scrollWidth, this.getViewportWidth());
1811     },
1812
1813     getViewportHeight: function() {
1814         var height = self.innerHeight;
1815         var mode = document.compatMode;
1816
1817         if ((mode || Roo.isIE) && !Roo.isOpera) {
1818             height = (mode == "CSS1Compat") ?
1819                      document.documentElement.clientHeight :
1820                      document.body.clientHeight;
1821         }
1822
1823         return height;
1824     },
1825
1826     getViewportWidth: function() {
1827         var width = self.innerWidth;
1828         var mode = document.compatMode;
1829
1830         if (mode || Roo.isIE) {
1831             width = (mode == "CSS1Compat") ?
1832                     document.documentElement.clientWidth :
1833                     document.body.clientWidth;
1834         }
1835         return width;
1836     },
1837
1838     isAncestor : function(p, c) {
1839         p = Roo.getDom(p);
1840         c = Roo.getDom(c);
1841         if (!p || !c) {
1842             return false;
1843         }
1844
1845         if (p.contains && !Roo.isSafari) {
1846             return p.contains(c);
1847         } else if (p.compareDocumentPosition) {
1848             return !!(p.compareDocumentPosition(c) & 16);
1849         } else {
1850             var parent = c.parentNode;
1851             while (parent) {
1852                 if (parent == p) {
1853                     return true;
1854                 }
1855                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1856                     return false;
1857                 }
1858                 parent = parent.parentNode;
1859             }
1860             return false;
1861         }
1862     },
1863
1864     getRegion : function(el) {
1865         return Roo.lib.Region.getRegion(el);
1866     },
1867
1868     getY : function(el) {
1869         return this.getXY(el)[1];
1870     },
1871
1872     getX : function(el) {
1873         return this.getXY(el)[0];
1874     },
1875
1876     getXY : function(el) {
1877         var p, pe, b, scroll, bd = document.body;
1878         el = Roo.getDom(el);
1879         var fly = Roo.lib.AnimBase.fly;
1880         if (el.getBoundingClientRect) {
1881             b = el.getBoundingClientRect();
1882             scroll = fly(document).getScroll();
1883             return [b.left + scroll.left, b.top + scroll.top];
1884         }
1885         var x = 0, y = 0;
1886
1887         p = el;
1888
1889         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1890
1891         while (p) {
1892
1893             x += p.offsetLeft;
1894             y += p.offsetTop;
1895
1896             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1897                 hasAbsolute = true;
1898             }
1899
1900             if (Roo.isGecko) {
1901                 pe = fly(p);
1902
1903                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1904                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1905
1906
1907                 x += bl;
1908                 y += bt;
1909
1910
1911                 if (p != el && pe.getStyle('overflow') != 'visible') {
1912                     x += bl;
1913                     y += bt;
1914                 }
1915             }
1916             p = p.offsetParent;
1917         }
1918
1919         if (Roo.isSafari && hasAbsolute) {
1920             x -= bd.offsetLeft;
1921             y -= bd.offsetTop;
1922         }
1923
1924         if (Roo.isGecko && !hasAbsolute) {
1925             var dbd = fly(bd);
1926             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1927             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1928         }
1929
1930         p = el.parentNode;
1931         while (p && p != bd) {
1932             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1933                 x -= p.scrollLeft;
1934                 y -= p.scrollTop;
1935             }
1936             p = p.parentNode;
1937         }
1938         return [x, y];
1939     },
1940  
1941   
1942
1943
1944     setXY : function(el, xy) {
1945         el = Roo.fly(el, '_setXY');
1946         el.position();
1947         var pts = el.translatePoints(xy);
1948         if (xy[0] !== false) {
1949             el.dom.style.left = pts.left + "px";
1950         }
1951         if (xy[1] !== false) {
1952             el.dom.style.top = pts.top + "px";
1953         }
1954     },
1955
1956     setX : function(el, x) {
1957         this.setXY(el, [x, false]);
1958     },
1959
1960     setY : function(el, y) {
1961         this.setXY(el, [false, y]);
1962     }
1963 };
1964 /*
1965  * Portions of this file are based on pieces of Yahoo User Interface Library
1966  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1967  * YUI licensed under the BSD License:
1968  * http://developer.yahoo.net/yui/license.txt
1969  * <script type="text/javascript">
1970  *
1971  */
1972
1973 Roo.lib.Event = function() {
1974     var loadComplete = false;
1975     var listeners = [];
1976     var unloadListeners = [];
1977     var retryCount = 0;
1978     var onAvailStack = [];
1979     var counter = 0;
1980     var lastError = null;
1981
1982     return {
1983         POLL_RETRYS: 200,
1984         POLL_INTERVAL: 20,
1985         EL: 0,
1986         TYPE: 1,
1987         FN: 2,
1988         WFN: 3,
1989         OBJ: 3,
1990         ADJ_SCOPE: 4,
1991         _interval: null,
1992
1993         startInterval: function() {
1994             if (!this._interval) {
1995                 var self = this;
1996                 var callback = function() {
1997                     self._tryPreloadAttach();
1998                 };
1999                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2000
2001             }
2002         },
2003
2004         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2005             onAvailStack.push({ id:         p_id,
2006                 fn:         p_fn,
2007                 obj:        p_obj,
2008                 override:   p_override,
2009                 checkReady: false    });
2010
2011             retryCount = this.POLL_RETRYS;
2012             this.startInterval();
2013         },
2014
2015
2016         addListener: function(el, eventName, fn) {
2017             el = Roo.getDom(el);
2018             if (!el || !fn) {
2019                 return false;
2020             }
2021
2022             if ("unload" == eventName) {
2023                 unloadListeners[unloadListeners.length] =
2024                 [el, eventName, fn];
2025                 return true;
2026             }
2027
2028             var wrappedFn = function(e) {
2029                 return fn(Roo.lib.Event.getEvent(e));
2030             };
2031
2032             var li = [el, eventName, fn, wrappedFn];
2033
2034             var index = listeners.length;
2035             listeners[index] = li;
2036
2037             this.doAdd(el, eventName, wrappedFn, false);
2038             return true;
2039
2040         },
2041
2042
2043         removeListener: function(el, eventName, fn) {
2044             var i, len;
2045
2046             el = Roo.getDom(el);
2047
2048             if(!fn) {
2049                 return this.purgeElement(el, false, eventName);
2050             }
2051
2052
2053             if ("unload" == eventName) {
2054
2055                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2056                     var li = unloadListeners[i];
2057                     if (li &&
2058                         li[0] == el &&
2059                         li[1] == eventName &&
2060                         li[2] == fn) {
2061                         unloadListeners.splice(i, 1);
2062                         return true;
2063                     }
2064                 }
2065
2066                 return false;
2067             }
2068
2069             var cacheItem = null;
2070
2071
2072             var index = arguments[3];
2073
2074             if ("undefined" == typeof index) {
2075                 index = this._getCacheIndex(el, eventName, fn);
2076             }
2077
2078             if (index >= 0) {
2079                 cacheItem = listeners[index];
2080             }
2081
2082             if (!el || !cacheItem) {
2083                 return false;
2084             }
2085
2086             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2087
2088             delete listeners[index][this.WFN];
2089             delete listeners[index][this.FN];
2090             listeners.splice(index, 1);
2091
2092             return true;
2093
2094         },
2095
2096
2097         getTarget: function(ev, resolveTextNode) {
2098             ev = ev.browserEvent || ev;
2099             var t = ev.target || ev.srcElement;
2100             return this.resolveTextNode(t);
2101         },
2102
2103
2104         resolveTextNode: function(node) {
2105             if (Roo.isSafari && node && 3 == node.nodeType) {
2106                 return node.parentNode;
2107             } else {
2108                 return node;
2109             }
2110         },
2111
2112
2113         getPageX: function(ev) {
2114             ev = ev.browserEvent || ev;
2115             var x = ev.pageX;
2116             if (!x && 0 !== x) {
2117                 x = ev.clientX || 0;
2118
2119                 if (Roo.isIE) {
2120                     x += this.getScroll()[1];
2121                 }
2122             }
2123
2124             return x;
2125         },
2126
2127
2128         getPageY: function(ev) {
2129             ev = ev.browserEvent || ev;
2130             var y = ev.pageY;
2131             if (!y && 0 !== y) {
2132                 y = ev.clientY || 0;
2133
2134                 if (Roo.isIE) {
2135                     y += this.getScroll()[0];
2136                 }
2137             }
2138
2139
2140             return y;
2141         },
2142
2143
2144         getXY: function(ev) {
2145             ev = ev.browserEvent || ev;
2146             return [this.getPageX(ev), this.getPageY(ev)];
2147         },
2148
2149
2150         getRelatedTarget: function(ev) {
2151             ev = ev.browserEvent || ev;
2152             var t = ev.relatedTarget;
2153             if (!t) {
2154                 if (ev.type == "mouseout") {
2155                     t = ev.toElement;
2156                 } else if (ev.type == "mouseover") {
2157                     t = ev.fromElement;
2158                 }
2159             }
2160
2161             return this.resolveTextNode(t);
2162         },
2163
2164
2165         getTime: function(ev) {
2166             ev = ev.browserEvent || ev;
2167             if (!ev.time) {
2168                 var t = new Date().getTime();
2169                 try {
2170                     ev.time = t;
2171                 } catch(ex) {
2172                     this.lastError = ex;
2173                     return t;
2174                 }
2175             }
2176
2177             return ev.time;
2178         },
2179
2180
2181         stopEvent: function(ev) {
2182             this.stopPropagation(ev);
2183             this.preventDefault(ev);
2184         },
2185
2186
2187         stopPropagation: function(ev) {
2188             ev = ev.browserEvent || ev;
2189             if (ev.stopPropagation) {
2190                 ev.stopPropagation();
2191             } else {
2192                 ev.cancelBubble = true;
2193             }
2194         },
2195
2196
2197         preventDefault: function(ev) {
2198             ev = ev.browserEvent || ev;
2199             if(ev.preventDefault) {
2200                 ev.preventDefault();
2201             } else {
2202                 ev.returnValue = false;
2203             }
2204         },
2205
2206
2207         getEvent: function(e) {
2208             var ev = e || window.event;
2209             if (!ev) {
2210                 var c = this.getEvent.caller;
2211                 while (c) {
2212                     ev = c.arguments[0];
2213                     if (ev && Event == ev.constructor) {
2214                         break;
2215                     }
2216                     c = c.caller;
2217                 }
2218             }
2219             return ev;
2220         },
2221
2222
2223         getCharCode: function(ev) {
2224             ev = ev.browserEvent || ev;
2225             return ev.charCode || ev.keyCode || 0;
2226         },
2227
2228
2229         _getCacheIndex: function(el, eventName, fn) {
2230             for (var i = 0,len = listeners.length; i < len; ++i) {
2231                 var li = listeners[i];
2232                 if (li &&
2233                     li[this.FN] == fn &&
2234                     li[this.EL] == el &&
2235                     li[this.TYPE] == eventName) {
2236                     return i;
2237                 }
2238             }
2239
2240             return -1;
2241         },
2242
2243
2244         elCache: {},
2245
2246
2247         getEl: function(id) {
2248             return document.getElementById(id);
2249         },
2250
2251
2252         clearCache: function() {
2253         },
2254
2255
2256         _load: function(e) {
2257             loadComplete = true;
2258             var EU = Roo.lib.Event;
2259
2260
2261             if (Roo.isIE) {
2262                 EU.doRemove(window, "load", EU._load);
2263             }
2264         },
2265
2266
2267         _tryPreloadAttach: function() {
2268
2269             if (this.locked) {
2270                 return false;
2271             }
2272
2273             this.locked = true;
2274
2275
2276             var tryAgain = !loadComplete;
2277             if (!tryAgain) {
2278                 tryAgain = (retryCount > 0);
2279             }
2280
2281
2282             var notAvail = [];
2283             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2284                 var item = onAvailStack[i];
2285                 if (item) {
2286                     var el = this.getEl(item.id);
2287
2288                     if (el) {
2289                         if (!item.checkReady ||
2290                             loadComplete ||
2291                             el.nextSibling ||
2292                             (document && document.body)) {
2293
2294                             var scope = el;
2295                             if (item.override) {
2296                                 if (item.override === true) {
2297                                     scope = item.obj;
2298                                 } else {
2299                                     scope = item.override;
2300                                 }
2301                             }
2302                             item.fn.call(scope, item.obj);
2303                             onAvailStack[i] = null;
2304                         }
2305                     } else {
2306                         notAvail.push(item);
2307                     }
2308                 }
2309             }
2310
2311             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2312
2313             if (tryAgain) {
2314
2315                 this.startInterval();
2316             } else {
2317                 clearInterval(this._interval);
2318                 this._interval = null;
2319             }
2320
2321             this.locked = false;
2322
2323             return true;
2324
2325         },
2326
2327
2328         purgeElement: function(el, recurse, eventName) {
2329             var elListeners = this.getListeners(el, eventName);
2330             if (elListeners) {
2331                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2332                     var l = elListeners[i];
2333                     this.removeListener(el, l.type, l.fn);
2334                 }
2335             }
2336
2337             if (recurse && el && el.childNodes) {
2338                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2339                     this.purgeElement(el.childNodes[i], recurse, eventName);
2340                 }
2341             }
2342         },
2343
2344
2345         getListeners: function(el, eventName) {
2346             var results = [], searchLists;
2347             if (!eventName) {
2348                 searchLists = [listeners, unloadListeners];
2349             } else if (eventName == "unload") {
2350                 searchLists = [unloadListeners];
2351             } else {
2352                 searchLists = [listeners];
2353             }
2354
2355             for (var j = 0; j < searchLists.length; ++j) {
2356                 var searchList = searchLists[j];
2357                 if (searchList && searchList.length > 0) {
2358                     for (var i = 0,len = searchList.length; i < len; ++i) {
2359                         var l = searchList[i];
2360                         if (l && l[this.EL] === el &&
2361                             (!eventName || eventName === l[this.TYPE])) {
2362                             results.push({
2363                                 type:   l[this.TYPE],
2364                                 fn:     l[this.FN],
2365                                 obj:    l[this.OBJ],
2366                                 adjust: l[this.ADJ_SCOPE],
2367                                 index:  i
2368                             });
2369                         }
2370                     }
2371                 }
2372             }
2373
2374             return (results.length) ? results : null;
2375         },
2376
2377
2378         _unload: function(e) {
2379
2380             var EU = Roo.lib.Event, i, j, l, len, index;
2381
2382             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2383                 l = unloadListeners[i];
2384                 if (l) {
2385                     var scope = window;
2386                     if (l[EU.ADJ_SCOPE]) {
2387                         if (l[EU.ADJ_SCOPE] === true) {
2388                             scope = l[EU.OBJ];
2389                         } else {
2390                             scope = l[EU.ADJ_SCOPE];
2391                         }
2392                     }
2393                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2394                     unloadListeners[i] = null;
2395                     l = null;
2396                     scope = null;
2397                 }
2398             }
2399
2400             unloadListeners = null;
2401
2402             if (listeners && listeners.length > 0) {
2403                 j = listeners.length;
2404                 while (j) {
2405                     index = j - 1;
2406                     l = listeners[index];
2407                     if (l) {
2408                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2409                                 l[EU.FN], index);
2410                     }
2411                     j = j - 1;
2412                 }
2413                 l = null;
2414
2415                 EU.clearCache();
2416             }
2417
2418             EU.doRemove(window, "unload", EU._unload);
2419
2420         },
2421
2422
2423         getScroll: function() {
2424             var dd = document.documentElement, db = document.body;
2425             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2426                 return [dd.scrollTop, dd.scrollLeft];
2427             } else if (db) {
2428                 return [db.scrollTop, db.scrollLeft];
2429             } else {
2430                 return [0, 0];
2431             }
2432         },
2433
2434
2435         doAdd: function () {
2436             if (window.addEventListener) {
2437                 return function(el, eventName, fn, capture) {
2438                     el.addEventListener(eventName, fn, (capture));
2439                 };
2440             } else if (window.attachEvent) {
2441                 return function(el, eventName, fn, capture) {
2442                     el.attachEvent("on" + eventName, fn);
2443                 };
2444             } else {
2445                 return function() {
2446                 };
2447             }
2448         }(),
2449
2450
2451         doRemove: function() {
2452             if (window.removeEventListener) {
2453                 return function (el, eventName, fn, capture) {
2454                     el.removeEventListener(eventName, fn, (capture));
2455                 };
2456             } else if (window.detachEvent) {
2457                 return function (el, eventName, fn) {
2458                     el.detachEvent("on" + eventName, fn);
2459                 };
2460             } else {
2461                 return function() {
2462                 };
2463             }
2464         }()
2465     };
2466     
2467 }();
2468 (function() {     
2469    
2470     var E = Roo.lib.Event;
2471     E.on = E.addListener;
2472     E.un = E.removeListener;
2473
2474     if (document && document.body) {
2475         E._load();
2476     } else {
2477         E.doAdd(window, "load", E._load);
2478     }
2479     E.doAdd(window, "unload", E._unload);
2480     E._tryPreloadAttach();
2481 })();
2482
2483 /*
2484  * Portions of this file are based on pieces of Yahoo User Interface Library
2485  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2486  * YUI licensed under the BSD License:
2487  * http://developer.yahoo.net/yui/license.txt
2488  * <script type="text/javascript">
2489  *
2490  */
2491
2492 (function() {
2493     /**
2494      * @class Roo.lib.Ajax
2495      *
2496      */
2497     Roo.lib.Ajax = {
2498         /**
2499          * @static 
2500          */
2501         request : function(method, uri, cb, data, options) {
2502             if(options){
2503                 var hs = options.headers;
2504                 if(hs){
2505                     for(var h in hs){
2506                         if(hs.hasOwnProperty(h)){
2507                             this.initHeader(h, hs[h], false);
2508                         }
2509                     }
2510                 }
2511                 if(options.xmlData){
2512                     this.initHeader('Content-Type', 'text/xml', false);
2513                     method = 'POST';
2514                     data = options.xmlData;
2515                 }
2516             }
2517
2518             return this.asyncRequest(method, uri, cb, data);
2519         },
2520
2521         serializeForm : function(form) {
2522             if(typeof form == 'string') {
2523                 form = (document.getElementById(form) || document.forms[form]);
2524             }
2525
2526             var el, name, val, disabled, data = '', hasSubmit = false;
2527             for (var i = 0; i < form.elements.length; i++) {
2528                 el = form.elements[i];
2529                 disabled = form.elements[i].disabled;
2530                 name = form.elements[i].name;
2531                 val = form.elements[i].value;
2532
2533                 if (!disabled && name){
2534                     switch (el.type)
2535                             {
2536                         case 'select-one':
2537                         case 'select-multiple':
2538                             for (var j = 0; j < el.options.length; j++) {
2539                                 if (el.options[j].selected) {
2540                                     if (Roo.isIE) {
2541                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2542                                     }
2543                                     else {
2544                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2545                                     }
2546                                 }
2547                             }
2548                             break;
2549                         case 'radio':
2550                         case 'checkbox':
2551                             if (el.checked) {
2552                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             }
2554                             break;
2555                         case 'file':
2556
2557                         case undefined:
2558
2559                         case 'reset':
2560
2561                         case 'button':
2562
2563                             break;
2564                         case 'submit':
2565                             if(hasSubmit == false) {
2566                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2567                                 hasSubmit = true;
2568                             }
2569                             break;
2570                         default:
2571                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2572                             break;
2573                     }
2574                 }
2575             }
2576             data = data.substr(0, data.length - 1);
2577             return data;
2578         },
2579
2580         headers:{},
2581
2582         hasHeaders:false,
2583
2584         useDefaultHeader:true,
2585
2586         defaultPostHeader:'application/x-www-form-urlencoded',
2587
2588         useDefaultXhrHeader:true,
2589
2590         defaultXhrHeader:'XMLHttpRequest',
2591
2592         hasDefaultHeaders:true,
2593
2594         defaultHeaders:{},
2595
2596         poll:{},
2597
2598         timeout:{},
2599
2600         pollInterval:50,
2601
2602         transactionId:0,
2603
2604         setProgId:function(id)
2605         {
2606             this.activeX.unshift(id);
2607         },
2608
2609         setDefaultPostHeader:function(b)
2610         {
2611             this.useDefaultHeader = b;
2612         },
2613
2614         setDefaultXhrHeader:function(b)
2615         {
2616             this.useDefaultXhrHeader = b;
2617         },
2618
2619         setPollingInterval:function(i)
2620         {
2621             if (typeof i == 'number' && isFinite(i)) {
2622                 this.pollInterval = i;
2623             }
2624         },
2625
2626         createXhrObject:function(transactionId)
2627         {
2628             var obj,http;
2629             try
2630             {
2631
2632                 http = new XMLHttpRequest();
2633
2634                 obj = { conn:http, tId:transactionId };
2635             }
2636             catch(e)
2637             {
2638                 for (var i = 0; i < this.activeX.length; ++i) {
2639                     try
2640                     {
2641
2642                         http = new ActiveXObject(this.activeX[i]);
2643
2644                         obj = { conn:http, tId:transactionId };
2645                         break;
2646                     }
2647                     catch(e) {
2648                     }
2649                 }
2650             }
2651             finally
2652             {
2653                 return obj;
2654             }
2655         },
2656
2657         getConnectionObject:function()
2658         {
2659             var o;
2660             var tId = this.transactionId;
2661
2662             try
2663             {
2664                 o = this.createXhrObject(tId);
2665                 if (o) {
2666                     this.transactionId++;
2667                 }
2668             }
2669             catch(e) {
2670             }
2671             finally
2672             {
2673                 return o;
2674             }
2675         },
2676
2677         asyncRequest:function(method, uri, callback, postData)
2678         {
2679             var o = this.getConnectionObject();
2680
2681             if (!o) {
2682                 return null;
2683             }
2684             else {
2685                 o.conn.open(method, uri, true);
2686
2687                 if (this.useDefaultXhrHeader) {
2688                     if (!this.defaultHeaders['X-Requested-With']) {
2689                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2690                     }
2691                 }
2692
2693                 if(postData && this.useDefaultHeader){
2694                     this.initHeader('Content-Type', this.defaultPostHeader);
2695                 }
2696
2697                  if (this.hasDefaultHeaders || this.hasHeaders) {
2698                     this.setHeader(o);
2699                 }
2700
2701                 this.handleReadyState(o, callback);
2702                 o.conn.send(postData || null);
2703
2704                 return o;
2705             }
2706         },
2707
2708         handleReadyState:function(o, callback)
2709         {
2710             var oConn = this;
2711
2712             if (callback && callback.timeout) {
2713                 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 " + 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 : 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      * @cfg {Boolean} multiSelect Allow multiple selection
24277      */
24278     multiSelect : false,
24279     /**
24280      * @cfg {Boolean} singleSelect Allow single selection
24281      */
24282     singleSelect:  false,
24283     
24284     /**
24285      * @cfg {Boolean} toggleSelect - selecting 
24286      */
24287     toggleSelect : false,
24288     
24289     /**
24290      * Returns the element this view is bound to.
24291      * @return {Roo.Element}
24292      */
24293     getEl : function(){
24294         return this.el;
24295     },
24296
24297     /**
24298      * Refreshes the view.
24299      */
24300     refresh : function(){
24301         var t = this.tpl;
24302         
24303         // if we are using something like 'domtemplate', then
24304         // the what gets used is:
24305         // t.applySubtemplate(NAME, data, wrapping data..)
24306         // the outer template then get' applied with
24307         //     the store 'extra data'
24308         // and the body get's added to the
24309         //      roo-name="data" node?
24310         //      <span class='roo-tpl-{name}'></span> ?????
24311         
24312         
24313         
24314         this.clearSelections();
24315         this.el.update("");
24316         var html = [];
24317         var records = this.store.getRange();
24318         if(records.length < 1) {
24319             
24320             // is this valid??  = should it render a template??
24321             
24322             this.el.update(this.emptyText);
24323             return;
24324         }
24325         var el = this.el;
24326         if (this.dataName) {
24327             this.el.update(t.apply(this.store.meta)); //????
24328             el = this.el.child('.roo-tpl-' + this.dataName);
24329         }
24330         
24331         for(var i = 0, len = records.length; i < len; i++){
24332             var data = this.prepareData(records[i].data, i, records[i]);
24333             this.fireEvent("preparedata", this, data, i, records[i]);
24334             html[html.length] = Roo.util.Format.trim(
24335                 this.dataName ?
24336                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24337                     t.apply(data)
24338             );
24339         }
24340         
24341         
24342         
24343         el.update(html.join(""));
24344         this.nodes = el.dom.childNodes;
24345         this.updateIndexes(0);
24346     },
24347
24348     /**
24349      * Function to override to reformat the data that is sent to
24350      * the template for each node.
24351      * DEPRICATED - use the preparedata event handler.
24352      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24353      * a JSON object for an UpdateManager bound view).
24354      */
24355     prepareData : function(data, index, record)
24356     {
24357         this.fireEvent("preparedata", this, data, index, record);
24358         return data;
24359     },
24360
24361     onUpdate : function(ds, record){
24362         this.clearSelections();
24363         var index = this.store.indexOf(record);
24364         var n = this.nodes[index];
24365         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24366         n.parentNode.removeChild(n);
24367         this.updateIndexes(index, index);
24368     },
24369
24370     
24371     
24372 // --------- FIXME     
24373     onAdd : function(ds, records, index)
24374     {
24375         this.clearSelections();
24376         if(this.nodes.length == 0){
24377             this.refresh();
24378             return;
24379         }
24380         var n = this.nodes[index];
24381         for(var i = 0, len = records.length; i < len; i++){
24382             var d = this.prepareData(records[i].data, i, records[i]);
24383             if(n){
24384                 this.tpl.insertBefore(n, d);
24385             }else{
24386                 
24387                 this.tpl.append(this.el, d);
24388             }
24389         }
24390         this.updateIndexes(index);
24391     },
24392
24393     onRemove : function(ds, record, index){
24394         this.clearSelections();
24395         var el = this.dataName  ?
24396             this.el.child('.roo-tpl-' + this.dataName) :
24397             this.el; 
24398         el.dom.removeChild(this.nodes[index]);
24399         this.updateIndexes(index);
24400     },
24401
24402     /**
24403      * Refresh an individual node.
24404      * @param {Number} index
24405      */
24406     refreshNode : function(index){
24407         this.onUpdate(this.store, this.store.getAt(index));
24408     },
24409
24410     updateIndexes : function(startIndex, endIndex){
24411         var ns = this.nodes;
24412         startIndex = startIndex || 0;
24413         endIndex = endIndex || ns.length - 1;
24414         for(var i = startIndex; i <= endIndex; i++){
24415             ns[i].nodeIndex = i;
24416         }
24417     },
24418
24419     /**
24420      * Changes the data store this view uses and refresh the view.
24421      * @param {Store} store
24422      */
24423     setStore : function(store, initial){
24424         if(!initial && this.store){
24425             this.store.un("datachanged", this.refresh);
24426             this.store.un("add", this.onAdd);
24427             this.store.un("remove", this.onRemove);
24428             this.store.un("update", this.onUpdate);
24429             this.store.un("clear", this.refresh);
24430         }
24431         if(store){
24432           
24433             store.on("datachanged", this.refresh, this);
24434             store.on("add", this.onAdd, this);
24435             store.on("remove", this.onRemove, this);
24436             store.on("update", this.onUpdate, this);
24437             store.on("clear", this.refresh, this);
24438         }
24439         
24440         if(store){
24441             this.refresh();
24442         }
24443     },
24444
24445     /**
24446      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
24447      * @param {HTMLElement} node
24448      * @return {HTMLElement} The template node
24449      */
24450     findItemFromChild : function(node){
24451         var el = this.dataName  ?
24452             this.el.child('.roo-tpl-' + this.dataName,true) :
24453             this.el.dom; 
24454         
24455         if(!node || node.parentNode == el){
24456                     return node;
24457             }
24458             var p = node.parentNode;
24459             while(p && p != el){
24460             if(p.parentNode == el){
24461                 return p;
24462             }
24463             p = p.parentNode;
24464         }
24465             return null;
24466     },
24467
24468     /** @ignore */
24469     onClick : function(e){
24470         var item = this.findItemFromChild(e.getTarget());
24471         if(item){
24472             var index = this.indexOf(item);
24473             if(this.onItemClick(item, index, e) !== false){
24474                 this.fireEvent("click", this, index, item, e);
24475             }
24476         }else{
24477             this.clearSelections();
24478         }
24479     },
24480
24481     /** @ignore */
24482     onContextMenu : function(e){
24483         var item = this.findItemFromChild(e.getTarget());
24484         if(item){
24485             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
24486         }
24487     },
24488
24489     /** @ignore */
24490     onDblClick : function(e){
24491         var item = this.findItemFromChild(e.getTarget());
24492         if(item){
24493             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
24494         }
24495     },
24496
24497     onItemClick : function(item, index, e)
24498     {
24499         if(this.fireEvent("beforeclick", this, index, item, e) === false){
24500             return false;
24501         }
24502         if (this.toggleSelect) {
24503             var m = this.isSelected(item) ? 'unselect' : 'select';
24504             Roo.log(m);
24505             var _t = this;
24506             _t[m](item, true, false);
24507             return true;
24508         }
24509         if(this.multiSelect || this.singleSelect){
24510             if(this.multiSelect && e.shiftKey && this.lastSelection){
24511                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
24512             }else{
24513                 this.select(item, this.multiSelect && e.ctrlKey);
24514                 this.lastSelection = item;
24515             }
24516             e.preventDefault();
24517         }
24518         return true;
24519     },
24520
24521     /**
24522      * Get the number of selected nodes.
24523      * @return {Number}
24524      */
24525     getSelectionCount : function(){
24526         return this.selections.length;
24527     },
24528
24529     /**
24530      * Get the currently selected nodes.
24531      * @return {Array} An array of HTMLElements
24532      */
24533     getSelectedNodes : function(){
24534         return this.selections;
24535     },
24536
24537     /**
24538      * Get the indexes of the selected nodes.
24539      * @return {Array}
24540      */
24541     getSelectedIndexes : function(){
24542         var indexes = [], s = this.selections;
24543         for(var i = 0, len = s.length; i < len; i++){
24544             indexes.push(s[i].nodeIndex);
24545         }
24546         return indexes;
24547     },
24548
24549     /**
24550      * Clear all selections
24551      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
24552      */
24553     clearSelections : function(suppressEvent){
24554         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24555             this.cmp.elements = this.selections;
24556             this.cmp.removeClass(this.selectedClass);
24557             this.selections = [];
24558             if(!suppressEvent){
24559                 this.fireEvent("selectionchange", this, this.selections);
24560             }
24561         }
24562     },
24563
24564     /**
24565      * Returns true if the passed node is selected
24566      * @param {HTMLElement/Number} node The node or node index
24567      * @return {Boolean}
24568      */
24569     isSelected : function(node){
24570         var s = this.selections;
24571         if(s.length < 1){
24572             return false;
24573         }
24574         node = this.getNode(node);
24575         return s.indexOf(node) !== -1;
24576     },
24577
24578     /**
24579      * Selects nodes.
24580      * @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
24581      * @param {Boolean} keepExisting (optional) true to keep existing selections
24582      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24583      */
24584     select : function(nodeInfo, keepExisting, suppressEvent){
24585         if(nodeInfo instanceof Array){
24586             if(!keepExisting){
24587                 this.clearSelections(true);
24588             }
24589             for(var i = 0, len = nodeInfo.length; i < len; i++){
24590                 this.select(nodeInfo[i], true, true);
24591             }
24592             return;
24593         } 
24594         var node = this.getNode(nodeInfo);
24595         if(!node || this.isSelected(node)){
24596             return; // already selected.
24597         }
24598         if(!keepExisting){
24599             this.clearSelections(true);
24600         }
24601         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24602             Roo.fly(node).addClass(this.selectedClass);
24603             this.selections.push(node);
24604             if(!suppressEvent){
24605                 this.fireEvent("selectionchange", this, this.selections);
24606             }
24607         }
24608         
24609         
24610     },
24611       /**
24612      * Unselects nodes.
24613      * @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
24614      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24615      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24616      */
24617     unselect : function(nodeInfo, keepExisting, suppressEvent)
24618     {
24619         if(nodeInfo instanceof Array){
24620             Roo.each(this.selections, function(s) {
24621                 this.unselect(s, nodeInfo);
24622             }, this);
24623             return;
24624         }
24625         var node = this.getNode(nodeInfo);
24626         if(!node || !this.isSelected(node)){
24627             Roo.log("not selected");
24628             return; // not selected.
24629         }
24630         // fireevent???
24631         var ns = [];
24632         Roo.each(this.selections, function(s) {
24633             if (s == node ) {
24634                 Roo.fly(node).removeClass(this.selectedClass);
24635
24636                 return;
24637             }
24638             ns.push(s);
24639         },this);
24640         
24641         this.selections= ns;
24642         this.fireEvent("selectionchange", this, this.selections);
24643     },
24644
24645     /**
24646      * Gets a template node.
24647      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24648      * @return {HTMLElement} The node or null if it wasn't found
24649      */
24650     getNode : function(nodeInfo){
24651         if(typeof nodeInfo == "string"){
24652             return document.getElementById(nodeInfo);
24653         }else if(typeof nodeInfo == "number"){
24654             return this.nodes[nodeInfo];
24655         }
24656         return nodeInfo;
24657     },
24658
24659     /**
24660      * Gets a range template nodes.
24661      * @param {Number} startIndex
24662      * @param {Number} endIndex
24663      * @return {Array} An array of nodes
24664      */
24665     getNodes : function(start, end){
24666         var ns = this.nodes;
24667         start = start || 0;
24668         end = typeof end == "undefined" ? ns.length - 1 : end;
24669         var nodes = [];
24670         if(start <= end){
24671             for(var i = start; i <= end; i++){
24672                 nodes.push(ns[i]);
24673             }
24674         } else{
24675             for(var i = start; i >= end; i--){
24676                 nodes.push(ns[i]);
24677             }
24678         }
24679         return nodes;
24680     },
24681
24682     /**
24683      * Finds the index of the passed node
24684      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24685      * @return {Number} The index of the node or -1
24686      */
24687     indexOf : function(node){
24688         node = this.getNode(node);
24689         if(typeof node.nodeIndex == "number"){
24690             return node.nodeIndex;
24691         }
24692         var ns = this.nodes;
24693         for(var i = 0, len = ns.length; i < len; i++){
24694             if(ns[i] == node){
24695                 return i;
24696             }
24697         }
24698         return -1;
24699     }
24700 });
24701 /*
24702  * Based on:
24703  * Ext JS Library 1.1.1
24704  * Copyright(c) 2006-2007, Ext JS, LLC.
24705  *
24706  * Originally Released Under LGPL - original licence link has changed is not relivant.
24707  *
24708  * Fork - LGPL
24709  * <script type="text/javascript">
24710  */
24711
24712 /**
24713  * @class Roo.JsonView
24714  * @extends Roo.View
24715  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24716 <pre><code>
24717 var view = new Roo.JsonView({
24718     container: "my-element",
24719     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24720     multiSelect: true, 
24721     jsonRoot: "data" 
24722 });
24723
24724 // listen for node click?
24725 view.on("click", function(vw, index, node, e){
24726     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24727 });
24728
24729 // direct load of JSON data
24730 view.load("foobar.php");
24731
24732 // Example from my blog list
24733 var tpl = new Roo.Template(
24734     '&lt;div class="entry"&gt;' +
24735     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24736     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24737     "&lt;/div&gt;&lt;hr /&gt;"
24738 );
24739
24740 var moreView = new Roo.JsonView({
24741     container :  "entry-list", 
24742     template : tpl,
24743     jsonRoot: "posts"
24744 });
24745 moreView.on("beforerender", this.sortEntries, this);
24746 moreView.load({
24747     url: "/blog/get-posts.php",
24748     params: "allposts=true",
24749     text: "Loading Blog Entries..."
24750 });
24751 </code></pre>
24752
24753 * Note: old code is supported with arguments : (container, template, config)
24754
24755
24756  * @constructor
24757  * Create a new JsonView
24758  * 
24759  * @param {Object} config The config object
24760  * 
24761  */
24762 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24763     
24764     
24765     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24766
24767     var um = this.el.getUpdateManager();
24768     um.setRenderer(this);
24769     um.on("update", this.onLoad, this);
24770     um.on("failure", this.onLoadException, this);
24771
24772     /**
24773      * @event beforerender
24774      * Fires before rendering of the downloaded JSON data.
24775      * @param {Roo.JsonView} this
24776      * @param {Object} data The JSON data loaded
24777      */
24778     /**
24779      * @event load
24780      * Fires when data is loaded.
24781      * @param {Roo.JsonView} this
24782      * @param {Object} data The JSON data loaded
24783      * @param {Object} response The raw Connect response object
24784      */
24785     /**
24786      * @event loadexception
24787      * Fires when loading fails.
24788      * @param {Roo.JsonView} this
24789      * @param {Object} response The raw Connect response object
24790      */
24791     this.addEvents({
24792         'beforerender' : true,
24793         'load' : true,
24794         'loadexception' : true
24795     });
24796 };
24797 Roo.extend(Roo.JsonView, Roo.View, {
24798     /**
24799      * @type {String} The root property in the loaded JSON object that contains the data
24800      */
24801     jsonRoot : "",
24802
24803     /**
24804      * Refreshes the view.
24805      */
24806     refresh : function(){
24807         this.clearSelections();
24808         this.el.update("");
24809         var html = [];
24810         var o = this.jsonData;
24811         if(o && o.length > 0){
24812             for(var i = 0, len = o.length; i < len; i++){
24813                 var data = this.prepareData(o[i], i, o);
24814                 html[html.length] = this.tpl.apply(data);
24815             }
24816         }else{
24817             html.push(this.emptyText);
24818         }
24819         this.el.update(html.join(""));
24820         this.nodes = this.el.dom.childNodes;
24821         this.updateIndexes(0);
24822     },
24823
24824     /**
24825      * 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.
24826      * @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:
24827      <pre><code>
24828      view.load({
24829          url: "your-url.php",
24830          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24831          callback: yourFunction,
24832          scope: yourObject, //(optional scope)
24833          discardUrl: false,
24834          nocache: false,
24835          text: "Loading...",
24836          timeout: 30,
24837          scripts: false
24838      });
24839      </code></pre>
24840      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24841      * 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.
24842      * @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}
24843      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24844      * @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.
24845      */
24846     load : function(){
24847         var um = this.el.getUpdateManager();
24848         um.update.apply(um, arguments);
24849     },
24850
24851     render : function(el, response){
24852         this.clearSelections();
24853         this.el.update("");
24854         var o;
24855         try{
24856             o = Roo.util.JSON.decode(response.responseText);
24857             if(this.jsonRoot){
24858                 
24859                 o = o[this.jsonRoot];
24860             }
24861         } catch(e){
24862         }
24863         /**
24864          * The current JSON data or null
24865          */
24866         this.jsonData = o;
24867         this.beforeRender();
24868         this.refresh();
24869     },
24870
24871 /**
24872  * Get the number of records in the current JSON dataset
24873  * @return {Number}
24874  */
24875     getCount : function(){
24876         return this.jsonData ? this.jsonData.length : 0;
24877     },
24878
24879 /**
24880  * Returns the JSON object for the specified node(s)
24881  * @param {HTMLElement/Array} node The node or an array of nodes
24882  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24883  * you get the JSON object for the node
24884  */
24885     getNodeData : function(node){
24886         if(node instanceof Array){
24887             var data = [];
24888             for(var i = 0, len = node.length; i < len; i++){
24889                 data.push(this.getNodeData(node[i]));
24890             }
24891             return data;
24892         }
24893         return this.jsonData[this.indexOf(node)] || null;
24894     },
24895
24896     beforeRender : function(){
24897         this.snapshot = this.jsonData;
24898         if(this.sortInfo){
24899             this.sort.apply(this, this.sortInfo);
24900         }
24901         this.fireEvent("beforerender", this, this.jsonData);
24902     },
24903
24904     onLoad : function(el, o){
24905         this.fireEvent("load", this, this.jsonData, o);
24906     },
24907
24908     onLoadException : function(el, o){
24909         this.fireEvent("loadexception", this, o);
24910     },
24911
24912 /**
24913  * Filter the data by a specific property.
24914  * @param {String} property A property on your JSON objects
24915  * @param {String/RegExp} value Either string that the property values
24916  * should start with, or a RegExp to test against the property
24917  */
24918     filter : function(property, value){
24919         if(this.jsonData){
24920             var data = [];
24921             var ss = this.snapshot;
24922             if(typeof value == "string"){
24923                 var vlen = value.length;
24924                 if(vlen == 0){
24925                     this.clearFilter();
24926                     return;
24927                 }
24928                 value = value.toLowerCase();
24929                 for(var i = 0, len = ss.length; i < len; i++){
24930                     var o = ss[i];
24931                     if(o[property].substr(0, vlen).toLowerCase() == value){
24932                         data.push(o);
24933                     }
24934                 }
24935             } else if(value.exec){ // regex?
24936                 for(var i = 0, len = ss.length; i < len; i++){
24937                     var o = ss[i];
24938                     if(value.test(o[property])){
24939                         data.push(o);
24940                     }
24941                 }
24942             } else{
24943                 return;
24944             }
24945             this.jsonData = data;
24946             this.refresh();
24947         }
24948     },
24949
24950 /**
24951  * Filter by a function. The passed function will be called with each
24952  * object in the current dataset. If the function returns true the value is kept,
24953  * otherwise it is filtered.
24954  * @param {Function} fn
24955  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24956  */
24957     filterBy : function(fn, scope){
24958         if(this.jsonData){
24959             var data = [];
24960             var ss = this.snapshot;
24961             for(var i = 0, len = ss.length; i < len; i++){
24962                 var o = ss[i];
24963                 if(fn.call(scope || this, o)){
24964                     data.push(o);
24965                 }
24966             }
24967             this.jsonData = data;
24968             this.refresh();
24969         }
24970     },
24971
24972 /**
24973  * Clears the current filter.
24974  */
24975     clearFilter : function(){
24976         if(this.snapshot && this.jsonData != this.snapshot){
24977             this.jsonData = this.snapshot;
24978             this.refresh();
24979         }
24980     },
24981
24982
24983 /**
24984  * Sorts the data for this view and refreshes it.
24985  * @param {String} property A property on your JSON objects to sort on
24986  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24987  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24988  */
24989     sort : function(property, dir, sortType){
24990         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24991         if(this.jsonData){
24992             var p = property;
24993             var dsc = dir && dir.toLowerCase() == "desc";
24994             var f = function(o1, o2){
24995                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24996                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24997                 ;
24998                 if(v1 < v2){
24999                     return dsc ? +1 : -1;
25000                 } else if(v1 > v2){
25001                     return dsc ? -1 : +1;
25002                 } else{
25003                     return 0;
25004                 }
25005             };
25006             this.jsonData.sort(f);
25007             this.refresh();
25008             if(this.jsonData != this.snapshot){
25009                 this.snapshot.sort(f);
25010             }
25011         }
25012     }
25013 });/*
25014  * Based on:
25015  * Ext JS Library 1.1.1
25016  * Copyright(c) 2006-2007, Ext JS, LLC.
25017  *
25018  * Originally Released Under LGPL - original licence link has changed is not relivant.
25019  *
25020  * Fork - LGPL
25021  * <script type="text/javascript">
25022  */
25023  
25024
25025 /**
25026  * @class Roo.ColorPalette
25027  * @extends Roo.Component
25028  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25029  * Here's an example of typical usage:
25030  * <pre><code>
25031 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25032 cp.render('my-div');
25033
25034 cp.on('select', function(palette, selColor){
25035     // do something with selColor
25036 });
25037 </code></pre>
25038  * @constructor
25039  * Create a new ColorPalette
25040  * @param {Object} config The config object
25041  */
25042 Roo.ColorPalette = function(config){
25043     Roo.ColorPalette.superclass.constructor.call(this, config);
25044     this.addEvents({
25045         /**
25046              * @event select
25047              * Fires when a color is selected
25048              * @param {ColorPalette} this
25049              * @param {String} color The 6-digit color hex code (without the # symbol)
25050              */
25051         select: true
25052     });
25053
25054     if(this.handler){
25055         this.on("select", this.handler, this.scope, true);
25056     }
25057 };
25058 Roo.extend(Roo.ColorPalette, Roo.Component, {
25059     /**
25060      * @cfg {String} itemCls
25061      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25062      */
25063     itemCls : "x-color-palette",
25064     /**
25065      * @cfg {String} value
25066      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25067      * the hex codes are case-sensitive.
25068      */
25069     value : null,
25070     clickEvent:'click',
25071     // private
25072     ctype: "Roo.ColorPalette",
25073
25074     /**
25075      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25076      */
25077     allowReselect : false,
25078
25079     /**
25080      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25081      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25082      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25083      * of colors with the width setting until the box is symmetrical.</p>
25084      * <p>You can override individual colors if needed:</p>
25085      * <pre><code>
25086 var cp = new Roo.ColorPalette();
25087 cp.colors[0] = "FF0000";  // change the first box to red
25088 </code></pre>
25089
25090 Or you can provide a custom array of your own for complete control:
25091 <pre><code>
25092 var cp = new Roo.ColorPalette();
25093 cp.colors = ["000000", "993300", "333300"];
25094 </code></pre>
25095      * @type Array
25096      */
25097     colors : [
25098         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25099         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25100         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25101         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25102         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25103     ],
25104
25105     // private
25106     onRender : function(container, position){
25107         var t = new Roo.MasterTemplate(
25108             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25109         );
25110         var c = this.colors;
25111         for(var i = 0, len = c.length; i < len; i++){
25112             t.add([c[i]]);
25113         }
25114         var el = document.createElement("div");
25115         el.className = this.itemCls;
25116         t.overwrite(el);
25117         container.dom.insertBefore(el, position);
25118         this.el = Roo.get(el);
25119         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25120         if(this.clickEvent != 'click'){
25121             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25122         }
25123     },
25124
25125     // private
25126     afterRender : function(){
25127         Roo.ColorPalette.superclass.afterRender.call(this);
25128         if(this.value){
25129             var s = this.value;
25130             this.value = null;
25131             this.select(s);
25132         }
25133     },
25134
25135     // private
25136     handleClick : function(e, t){
25137         e.preventDefault();
25138         if(!this.disabled){
25139             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25140             this.select(c.toUpperCase());
25141         }
25142     },
25143
25144     /**
25145      * Selects the specified color in the palette (fires the select event)
25146      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25147      */
25148     select : function(color){
25149         color = color.replace("#", "");
25150         if(color != this.value || this.allowReselect){
25151             var el = this.el;
25152             if(this.value){
25153                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25154             }
25155             el.child("a.color-"+color).addClass("x-color-palette-sel");
25156             this.value = color;
25157             this.fireEvent("select", this, color);
25158         }
25159     }
25160 });/*
25161  * Based on:
25162  * Ext JS Library 1.1.1
25163  * Copyright(c) 2006-2007, Ext JS, LLC.
25164  *
25165  * Originally Released Under LGPL - original licence link has changed is not relivant.
25166  *
25167  * Fork - LGPL
25168  * <script type="text/javascript">
25169  */
25170  
25171 /**
25172  * @class Roo.DatePicker
25173  * @extends Roo.Component
25174  * Simple date picker class.
25175  * @constructor
25176  * Create a new DatePicker
25177  * @param {Object} config The config object
25178  */
25179 Roo.DatePicker = function(config){
25180     Roo.DatePicker.superclass.constructor.call(this, config);
25181
25182     this.value = config && config.value ?
25183                  config.value.clearTime() : new Date().clearTime();
25184
25185     this.addEvents({
25186         /**
25187              * @event select
25188              * Fires when a date is selected
25189              * @param {DatePicker} this
25190              * @param {Date} date The selected date
25191              */
25192         'select': true,
25193         /**
25194              * @event monthchange
25195              * Fires when the displayed month changes 
25196              * @param {DatePicker} this
25197              * @param {Date} date The selected month
25198              */
25199         'monthchange': true
25200     });
25201
25202     if(this.handler){
25203         this.on("select", this.handler,  this.scope || this);
25204     }
25205     // build the disabledDatesRE
25206     if(!this.disabledDatesRE && this.disabledDates){
25207         var dd = this.disabledDates;
25208         var re = "(?:";
25209         for(var i = 0; i < dd.length; i++){
25210             re += dd[i];
25211             if(i != dd.length-1) re += "|";
25212         }
25213         this.disabledDatesRE = new RegExp(re + ")");
25214     }
25215 };
25216
25217 Roo.extend(Roo.DatePicker, Roo.Component, {
25218     /**
25219      * @cfg {String} todayText
25220      * The text to display on the button that selects the current date (defaults to "Today")
25221      */
25222     todayText : "Today",
25223     /**
25224      * @cfg {String} okText
25225      * The text to display on the ok button
25226      */
25227     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25228     /**
25229      * @cfg {String} cancelText
25230      * The text to display on the cancel button
25231      */
25232     cancelText : "Cancel",
25233     /**
25234      * @cfg {String} todayTip
25235      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25236      */
25237     todayTip : "{0} (Spacebar)",
25238     /**
25239      * @cfg {Date} minDate
25240      * Minimum allowable date (JavaScript date object, defaults to null)
25241      */
25242     minDate : null,
25243     /**
25244      * @cfg {Date} maxDate
25245      * Maximum allowable date (JavaScript date object, defaults to null)
25246      */
25247     maxDate : null,
25248     /**
25249      * @cfg {String} minText
25250      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25251      */
25252     minText : "This date is before the minimum date",
25253     /**
25254      * @cfg {String} maxText
25255      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25256      */
25257     maxText : "This date is after the maximum date",
25258     /**
25259      * @cfg {String} format
25260      * The default date format string which can be overriden for localization support.  The format must be
25261      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25262      */
25263     format : "m/d/y",
25264     /**
25265      * @cfg {Array} disabledDays
25266      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25267      */
25268     disabledDays : null,
25269     /**
25270      * @cfg {String} disabledDaysText
25271      * The tooltip to display when the date falls on a disabled day (defaults to "")
25272      */
25273     disabledDaysText : "",
25274     /**
25275      * @cfg {RegExp} disabledDatesRE
25276      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25277      */
25278     disabledDatesRE : null,
25279     /**
25280      * @cfg {String} disabledDatesText
25281      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25282      */
25283     disabledDatesText : "",
25284     /**
25285      * @cfg {Boolean} constrainToViewport
25286      * True to constrain the date picker to the viewport (defaults to true)
25287      */
25288     constrainToViewport : true,
25289     /**
25290      * @cfg {Array} monthNames
25291      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25292      */
25293     monthNames : Date.monthNames,
25294     /**
25295      * @cfg {Array} dayNames
25296      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25297      */
25298     dayNames : Date.dayNames,
25299     /**
25300      * @cfg {String} nextText
25301      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25302      */
25303     nextText: 'Next Month (Control+Right)',
25304     /**
25305      * @cfg {String} prevText
25306      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25307      */
25308     prevText: 'Previous Month (Control+Left)',
25309     /**
25310      * @cfg {String} monthYearText
25311      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25312      */
25313     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25314     /**
25315      * @cfg {Number} startDay
25316      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25317      */
25318     startDay : 0,
25319     /**
25320      * @cfg {Bool} showClear
25321      * Show a clear button (usefull for date form elements that can be blank.)
25322      */
25323     
25324     showClear: false,
25325     
25326     /**
25327      * Sets the value of the date field
25328      * @param {Date} value The date to set
25329      */
25330     setValue : function(value){
25331         var old = this.value;
25332         
25333         if (typeof(value) == 'string') {
25334          
25335             value = Date.parseDate(value, this.format);
25336         }
25337         if (!value) {
25338             value = new Date();
25339         }
25340         
25341         this.value = value.clearTime(true);
25342         if(this.el){
25343             this.update(this.value);
25344         }
25345     },
25346
25347     /**
25348      * Gets the current selected value of the date field
25349      * @return {Date} The selected date
25350      */
25351     getValue : function(){
25352         return this.value;
25353     },
25354
25355     // private
25356     focus : function(){
25357         if(this.el){
25358             this.update(this.activeDate);
25359         }
25360     },
25361
25362     // privateval
25363     onRender : function(container, position){
25364         
25365         var m = [
25366              '<table cellspacing="0">',
25367                 '<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>',
25368                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
25369         var dn = this.dayNames;
25370         for(var i = 0; i < 7; i++){
25371             var d = this.startDay+i;
25372             if(d > 6){
25373                 d = d-7;
25374             }
25375             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
25376         }
25377         m[m.length] = "</tr></thead><tbody><tr>";
25378         for(var i = 0; i < 42; i++) {
25379             if(i % 7 == 0 && i != 0){
25380                 m[m.length] = "</tr><tr>";
25381             }
25382             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
25383         }
25384         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
25385             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
25386
25387         var el = document.createElement("div");
25388         el.className = "x-date-picker";
25389         el.innerHTML = m.join("");
25390
25391         container.dom.insertBefore(el, position);
25392
25393         this.el = Roo.get(el);
25394         this.eventEl = Roo.get(el.firstChild);
25395
25396         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
25397             handler: this.showPrevMonth,
25398             scope: this,
25399             preventDefault:true,
25400             stopDefault:true
25401         });
25402
25403         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
25404             handler: this.showNextMonth,
25405             scope: this,
25406             preventDefault:true,
25407             stopDefault:true
25408         });
25409
25410         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
25411
25412         this.monthPicker = this.el.down('div.x-date-mp');
25413         this.monthPicker.enableDisplayMode('block');
25414         
25415         var kn = new Roo.KeyNav(this.eventEl, {
25416             "left" : function(e){
25417                 e.ctrlKey ?
25418                     this.showPrevMonth() :
25419                     this.update(this.activeDate.add("d", -1));
25420             },
25421
25422             "right" : function(e){
25423                 e.ctrlKey ?
25424                     this.showNextMonth() :
25425                     this.update(this.activeDate.add("d", 1));
25426             },
25427
25428             "up" : function(e){
25429                 e.ctrlKey ?
25430                     this.showNextYear() :
25431                     this.update(this.activeDate.add("d", -7));
25432             },
25433
25434             "down" : function(e){
25435                 e.ctrlKey ?
25436                     this.showPrevYear() :
25437                     this.update(this.activeDate.add("d", 7));
25438             },
25439
25440             "pageUp" : function(e){
25441                 this.showNextMonth();
25442             },
25443
25444             "pageDown" : function(e){
25445                 this.showPrevMonth();
25446             },
25447
25448             "enter" : function(e){
25449                 e.stopPropagation();
25450                 return true;
25451             },
25452
25453             scope : this
25454         });
25455
25456         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
25457
25458         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
25459
25460         this.el.unselectable();
25461         
25462         this.cells = this.el.select("table.x-date-inner tbody td");
25463         this.textNodes = this.el.query("table.x-date-inner tbody span");
25464
25465         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
25466             text: "&#160;",
25467             tooltip: this.monthYearText
25468         });
25469
25470         this.mbtn.on('click', this.showMonthPicker, this);
25471         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
25472
25473
25474         var today = (new Date()).dateFormat(this.format);
25475         
25476         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
25477         if (this.showClear) {
25478             baseTb.add( new Roo.Toolbar.Fill());
25479         }
25480         baseTb.add({
25481             text: String.format(this.todayText, today),
25482             tooltip: String.format(this.todayTip, today),
25483             handler: this.selectToday,
25484             scope: this
25485         });
25486         
25487         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
25488             
25489         //});
25490         if (this.showClear) {
25491             
25492             baseTb.add( new Roo.Toolbar.Fill());
25493             baseTb.add({
25494                 text: '&#160;',
25495                 cls: 'x-btn-icon x-btn-clear',
25496                 handler: function() {
25497                     //this.value = '';
25498                     this.fireEvent("select", this, '');
25499                 },
25500                 scope: this
25501             });
25502         }
25503         
25504         
25505         if(Roo.isIE){
25506             this.el.repaint();
25507         }
25508         this.update(this.value);
25509     },
25510
25511     createMonthPicker : function(){
25512         if(!this.monthPicker.dom.firstChild){
25513             var buf = ['<table border="0" cellspacing="0">'];
25514             for(var i = 0; i < 6; i++){
25515                 buf.push(
25516                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
25517                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
25518                     i == 0 ?
25519                     '<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>' :
25520                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
25521                 );
25522             }
25523             buf.push(
25524                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
25525                     this.okText,
25526                     '</button><button type="button" class="x-date-mp-cancel">',
25527                     this.cancelText,
25528                     '</button></td></tr>',
25529                 '</table>'
25530             );
25531             this.monthPicker.update(buf.join(''));
25532             this.monthPicker.on('click', this.onMonthClick, this);
25533             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
25534
25535             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
25536             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
25537
25538             this.mpMonths.each(function(m, a, i){
25539                 i += 1;
25540                 if((i%2) == 0){
25541                     m.dom.xmonth = 5 + Math.round(i * .5);
25542                 }else{
25543                     m.dom.xmonth = Math.round((i-1) * .5);
25544                 }
25545             });
25546         }
25547     },
25548
25549     showMonthPicker : function(){
25550         this.createMonthPicker();
25551         var size = this.el.getSize();
25552         this.monthPicker.setSize(size);
25553         this.monthPicker.child('table').setSize(size);
25554
25555         this.mpSelMonth = (this.activeDate || this.value).getMonth();
25556         this.updateMPMonth(this.mpSelMonth);
25557         this.mpSelYear = (this.activeDate || this.value).getFullYear();
25558         this.updateMPYear(this.mpSelYear);
25559
25560         this.monthPicker.slideIn('t', {duration:.2});
25561     },
25562
25563     updateMPYear : function(y){
25564         this.mpyear = y;
25565         var ys = this.mpYears.elements;
25566         for(var i = 1; i <= 10; i++){
25567             var td = ys[i-1], y2;
25568             if((i%2) == 0){
25569                 y2 = y + Math.round(i * .5);
25570                 td.firstChild.innerHTML = y2;
25571                 td.xyear = y2;
25572             }else{
25573                 y2 = y - (5-Math.round(i * .5));
25574                 td.firstChild.innerHTML = y2;
25575                 td.xyear = y2;
25576             }
25577             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25578         }
25579     },
25580
25581     updateMPMonth : function(sm){
25582         this.mpMonths.each(function(m, a, i){
25583             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25584         });
25585     },
25586
25587     selectMPMonth: function(m){
25588         
25589     },
25590
25591     onMonthClick : function(e, t){
25592         e.stopEvent();
25593         var el = new Roo.Element(t), pn;
25594         if(el.is('button.x-date-mp-cancel')){
25595             this.hideMonthPicker();
25596         }
25597         else if(el.is('button.x-date-mp-ok')){
25598             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25599             this.hideMonthPicker();
25600         }
25601         else if(pn = el.up('td.x-date-mp-month', 2)){
25602             this.mpMonths.removeClass('x-date-mp-sel');
25603             pn.addClass('x-date-mp-sel');
25604             this.mpSelMonth = pn.dom.xmonth;
25605         }
25606         else if(pn = el.up('td.x-date-mp-year', 2)){
25607             this.mpYears.removeClass('x-date-mp-sel');
25608             pn.addClass('x-date-mp-sel');
25609             this.mpSelYear = pn.dom.xyear;
25610         }
25611         else if(el.is('a.x-date-mp-prev')){
25612             this.updateMPYear(this.mpyear-10);
25613         }
25614         else if(el.is('a.x-date-mp-next')){
25615             this.updateMPYear(this.mpyear+10);
25616         }
25617     },
25618
25619     onMonthDblClick : function(e, t){
25620         e.stopEvent();
25621         var el = new Roo.Element(t), pn;
25622         if(pn = el.up('td.x-date-mp-month', 2)){
25623             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25624             this.hideMonthPicker();
25625         }
25626         else if(pn = el.up('td.x-date-mp-year', 2)){
25627             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25628             this.hideMonthPicker();
25629         }
25630     },
25631
25632     hideMonthPicker : function(disableAnim){
25633         if(this.monthPicker){
25634             if(disableAnim === true){
25635                 this.monthPicker.hide();
25636             }else{
25637                 this.monthPicker.slideOut('t', {duration:.2});
25638             }
25639         }
25640     },
25641
25642     // private
25643     showPrevMonth : function(e){
25644         this.update(this.activeDate.add("mo", -1));
25645     },
25646
25647     // private
25648     showNextMonth : function(e){
25649         this.update(this.activeDate.add("mo", 1));
25650     },
25651
25652     // private
25653     showPrevYear : function(){
25654         this.update(this.activeDate.add("y", -1));
25655     },
25656
25657     // private
25658     showNextYear : function(){
25659         this.update(this.activeDate.add("y", 1));
25660     },
25661
25662     // private
25663     handleMouseWheel : function(e){
25664         var delta = e.getWheelDelta();
25665         if(delta > 0){
25666             this.showPrevMonth();
25667             e.stopEvent();
25668         } else if(delta < 0){
25669             this.showNextMonth();
25670             e.stopEvent();
25671         }
25672     },
25673
25674     // private
25675     handleDateClick : function(e, t){
25676         e.stopEvent();
25677         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25678             this.setValue(new Date(t.dateValue));
25679             this.fireEvent("select", this, this.value);
25680         }
25681     },
25682
25683     // private
25684     selectToday : function(){
25685         this.setValue(new Date().clearTime());
25686         this.fireEvent("select", this, this.value);
25687     },
25688
25689     // private
25690     update : function(date)
25691     {
25692         var vd = this.activeDate;
25693         this.activeDate = date;
25694         if(vd && this.el){
25695             var t = date.getTime();
25696             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25697                 this.cells.removeClass("x-date-selected");
25698                 this.cells.each(function(c){
25699                    if(c.dom.firstChild.dateValue == t){
25700                        c.addClass("x-date-selected");
25701                        setTimeout(function(){
25702                             try{c.dom.firstChild.focus();}catch(e){}
25703                        }, 50);
25704                        return false;
25705                    }
25706                 });
25707                 return;
25708             }
25709         }
25710         
25711         var days = date.getDaysInMonth();
25712         var firstOfMonth = date.getFirstDateOfMonth();
25713         var startingPos = firstOfMonth.getDay()-this.startDay;
25714
25715         if(startingPos <= this.startDay){
25716             startingPos += 7;
25717         }
25718
25719         var pm = date.add("mo", -1);
25720         var prevStart = pm.getDaysInMonth()-startingPos;
25721
25722         var cells = this.cells.elements;
25723         var textEls = this.textNodes;
25724         days += startingPos;
25725
25726         // convert everything to numbers so it's fast
25727         var day = 86400000;
25728         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25729         var today = new Date().clearTime().getTime();
25730         var sel = date.clearTime().getTime();
25731         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25732         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25733         var ddMatch = this.disabledDatesRE;
25734         var ddText = this.disabledDatesText;
25735         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25736         var ddaysText = this.disabledDaysText;
25737         var format = this.format;
25738
25739         var setCellClass = function(cal, cell){
25740             cell.title = "";
25741             var t = d.getTime();
25742             cell.firstChild.dateValue = t;
25743             if(t == today){
25744                 cell.className += " x-date-today";
25745                 cell.title = cal.todayText;
25746             }
25747             if(t == sel){
25748                 cell.className += " x-date-selected";
25749                 setTimeout(function(){
25750                     try{cell.firstChild.focus();}catch(e){}
25751                 }, 50);
25752             }
25753             // disabling
25754             if(t < min) {
25755                 cell.className = " x-date-disabled";
25756                 cell.title = cal.minText;
25757                 return;
25758             }
25759             if(t > max) {
25760                 cell.className = " x-date-disabled";
25761                 cell.title = cal.maxText;
25762                 return;
25763             }
25764             if(ddays){
25765                 if(ddays.indexOf(d.getDay()) != -1){
25766                     cell.title = ddaysText;
25767                     cell.className = " x-date-disabled";
25768                 }
25769             }
25770             if(ddMatch && format){
25771                 var fvalue = d.dateFormat(format);
25772                 if(ddMatch.test(fvalue)){
25773                     cell.title = ddText.replace("%0", fvalue);
25774                     cell.className = " x-date-disabled";
25775                 }
25776             }
25777         };
25778
25779         var i = 0;
25780         for(; i < startingPos; i++) {
25781             textEls[i].innerHTML = (++prevStart);
25782             d.setDate(d.getDate()+1);
25783             cells[i].className = "x-date-prevday";
25784             setCellClass(this, cells[i]);
25785         }
25786         for(; i < days; i++){
25787             intDay = i - startingPos + 1;
25788             textEls[i].innerHTML = (intDay);
25789             d.setDate(d.getDate()+1);
25790             cells[i].className = "x-date-active";
25791             setCellClass(this, cells[i]);
25792         }
25793         var extraDays = 0;
25794         for(; i < 42; i++) {
25795              textEls[i].innerHTML = (++extraDays);
25796              d.setDate(d.getDate()+1);
25797              cells[i].className = "x-date-nextday";
25798              setCellClass(this, cells[i]);
25799         }
25800
25801         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25802         this.fireEvent('monthchange', this, date);
25803         
25804         if(!this.internalRender){
25805             var main = this.el.dom.firstChild;
25806             var w = main.offsetWidth;
25807             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25808             Roo.fly(main).setWidth(w);
25809             this.internalRender = true;
25810             // opera does not respect the auto grow header center column
25811             // then, after it gets a width opera refuses to recalculate
25812             // without a second pass
25813             if(Roo.isOpera && !this.secondPass){
25814                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25815                 this.secondPass = true;
25816                 this.update.defer(10, this, [date]);
25817             }
25818         }
25819         
25820         
25821     }
25822 });        /*
25823  * Based on:
25824  * Ext JS Library 1.1.1
25825  * Copyright(c) 2006-2007, Ext JS, LLC.
25826  *
25827  * Originally Released Under LGPL - original licence link has changed is not relivant.
25828  *
25829  * Fork - LGPL
25830  * <script type="text/javascript">
25831  */
25832 /**
25833  * @class Roo.TabPanel
25834  * @extends Roo.util.Observable
25835  * A lightweight tab container.
25836  * <br><br>
25837  * Usage:
25838  * <pre><code>
25839 // basic tabs 1, built from existing content
25840 var tabs = new Roo.TabPanel("tabs1");
25841 tabs.addTab("script", "View Script");
25842 tabs.addTab("markup", "View Markup");
25843 tabs.activate("script");
25844
25845 // more advanced tabs, built from javascript
25846 var jtabs = new Roo.TabPanel("jtabs");
25847 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25848
25849 // set up the UpdateManager
25850 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25851 var updater = tab2.getUpdateManager();
25852 updater.setDefaultUrl("ajax1.htm");
25853 tab2.on('activate', updater.refresh, updater, true);
25854
25855 // Use setUrl for Ajax loading
25856 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25857 tab3.setUrl("ajax2.htm", null, true);
25858
25859 // Disabled tab
25860 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25861 tab4.disable();
25862
25863 jtabs.activate("jtabs-1");
25864  * </code></pre>
25865  * @constructor
25866  * Create a new TabPanel.
25867  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25868  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25869  */
25870 Roo.TabPanel = function(container, config){
25871     /**
25872     * The container element for this TabPanel.
25873     * @type Roo.Element
25874     */
25875     this.el = Roo.get(container, true);
25876     if(config){
25877         if(typeof config == "boolean"){
25878             this.tabPosition = config ? "bottom" : "top";
25879         }else{
25880             Roo.apply(this, config);
25881         }
25882     }
25883     if(this.tabPosition == "bottom"){
25884         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25885         this.el.addClass("x-tabs-bottom");
25886     }
25887     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25888     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25889     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25890     if(Roo.isIE){
25891         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25892     }
25893     if(this.tabPosition != "bottom"){
25894         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25895          * @type Roo.Element
25896          */
25897         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25898         this.el.addClass("x-tabs-top");
25899     }
25900     this.items = [];
25901
25902     this.bodyEl.setStyle("position", "relative");
25903
25904     this.active = null;
25905     this.activateDelegate = this.activate.createDelegate(this);
25906
25907     this.addEvents({
25908         /**
25909          * @event tabchange
25910          * Fires when the active tab changes
25911          * @param {Roo.TabPanel} this
25912          * @param {Roo.TabPanelItem} activePanel The new active tab
25913          */
25914         "tabchange": true,
25915         /**
25916          * @event beforetabchange
25917          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25918          * @param {Roo.TabPanel} this
25919          * @param {Object} e Set cancel to true on this object to cancel the tab change
25920          * @param {Roo.TabPanelItem} tab The tab being changed to
25921          */
25922         "beforetabchange" : true
25923     });
25924
25925     Roo.EventManager.onWindowResize(this.onResize, this);
25926     this.cpad = this.el.getPadding("lr");
25927     this.hiddenCount = 0;
25928
25929
25930     // toolbar on the tabbar support...
25931     if (this.toolbar) {
25932         var tcfg = this.toolbar;
25933         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25934         this.toolbar = new Roo.Toolbar(tcfg);
25935         if (Roo.isSafari) {
25936             var tbl = tcfg.container.child('table', true);
25937             tbl.setAttribute('width', '100%');
25938         }
25939         
25940     }
25941    
25942
25943
25944     Roo.TabPanel.superclass.constructor.call(this);
25945 };
25946
25947 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25948     /*
25949      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25950      */
25951     tabPosition : "top",
25952     /*
25953      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25954      */
25955     currentTabWidth : 0,
25956     /*
25957      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25958      */
25959     minTabWidth : 40,
25960     /*
25961      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25962      */
25963     maxTabWidth : 250,
25964     /*
25965      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25966      */
25967     preferredTabWidth : 175,
25968     /*
25969      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25970      */
25971     resizeTabs : false,
25972     /*
25973      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25974      */
25975     monitorResize : true,
25976     /*
25977      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25978      */
25979     toolbar : false,
25980
25981     /**
25982      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25983      * @param {String} id The id of the div to use <b>or create</b>
25984      * @param {String} text The text for the tab
25985      * @param {String} content (optional) Content to put in the TabPanelItem body
25986      * @param {Boolean} closable (optional) True to create a close icon on the tab
25987      * @return {Roo.TabPanelItem} The created TabPanelItem
25988      */
25989     addTab : function(id, text, content, closable){
25990         var item = new Roo.TabPanelItem(this, id, text, closable);
25991         this.addTabItem(item);
25992         if(content){
25993             item.setContent(content);
25994         }
25995         return item;
25996     },
25997
25998     /**
25999      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26000      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26001      * @return {Roo.TabPanelItem}
26002      */
26003     getTab : function(id){
26004         return this.items[id];
26005     },
26006
26007     /**
26008      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26009      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26010      */
26011     hideTab : function(id){
26012         var t = this.items[id];
26013         if(!t.isHidden()){
26014            t.setHidden(true);
26015            this.hiddenCount++;
26016            this.autoSizeTabs();
26017         }
26018     },
26019
26020     /**
26021      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26022      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26023      */
26024     unhideTab : function(id){
26025         var t = this.items[id];
26026         if(t.isHidden()){
26027            t.setHidden(false);
26028            this.hiddenCount--;
26029            this.autoSizeTabs();
26030         }
26031     },
26032
26033     /**
26034      * Adds an existing {@link Roo.TabPanelItem}.
26035      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26036      */
26037     addTabItem : function(item){
26038         this.items[item.id] = item;
26039         this.items.push(item);
26040         if(this.resizeTabs){
26041            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26042            this.autoSizeTabs();
26043         }else{
26044             item.autoSize();
26045         }
26046     },
26047
26048     /**
26049      * Removes a {@link Roo.TabPanelItem}.
26050      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26051      */
26052     removeTab : function(id){
26053         var items = this.items;
26054         var tab = items[id];
26055         if(!tab) { return; }
26056         var index = items.indexOf(tab);
26057         if(this.active == tab && items.length > 1){
26058             var newTab = this.getNextAvailable(index);
26059             if(newTab) {
26060                 newTab.activate();
26061             }
26062         }
26063         this.stripEl.dom.removeChild(tab.pnode.dom);
26064         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26065             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26066         }
26067         items.splice(index, 1);
26068         delete this.items[tab.id];
26069         tab.fireEvent("close", tab);
26070         tab.purgeListeners();
26071         this.autoSizeTabs();
26072     },
26073
26074     getNextAvailable : function(start){
26075         var items = this.items;
26076         var index = start;
26077         // look for a next tab that will slide over to
26078         // replace the one being removed
26079         while(index < items.length){
26080             var item = items[++index];
26081             if(item && !item.isHidden()){
26082                 return item;
26083             }
26084         }
26085         // if one isn't found select the previous tab (on the left)
26086         index = start;
26087         while(index >= 0){
26088             var item = items[--index];
26089             if(item && !item.isHidden()){
26090                 return item;
26091             }
26092         }
26093         return null;
26094     },
26095
26096     /**
26097      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26098      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26099      */
26100     disableTab : function(id){
26101         var tab = this.items[id];
26102         if(tab && this.active != tab){
26103             tab.disable();
26104         }
26105     },
26106
26107     /**
26108      * Enables a {@link Roo.TabPanelItem} that is disabled.
26109      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26110      */
26111     enableTab : function(id){
26112         var tab = this.items[id];
26113         tab.enable();
26114     },
26115
26116     /**
26117      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26118      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26119      * @return {Roo.TabPanelItem} The TabPanelItem.
26120      */
26121     activate : function(id){
26122         var tab = this.items[id];
26123         if(!tab){
26124             return null;
26125         }
26126         if(tab == this.active || tab.disabled){
26127             return tab;
26128         }
26129         var e = {};
26130         this.fireEvent("beforetabchange", this, e, tab);
26131         if(e.cancel !== true && !tab.disabled){
26132             if(this.active){
26133                 this.active.hide();
26134             }
26135             this.active = this.items[id];
26136             this.active.show();
26137             this.fireEvent("tabchange", this, this.active);
26138         }
26139         return tab;
26140     },
26141
26142     /**
26143      * Gets the active {@link Roo.TabPanelItem}.
26144      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26145      */
26146     getActiveTab : function(){
26147         return this.active;
26148     },
26149
26150     /**
26151      * Updates the tab body element to fit the height of the container element
26152      * for overflow scrolling
26153      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26154      */
26155     syncHeight : function(targetHeight){
26156         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26157         var bm = this.bodyEl.getMargins();
26158         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26159         this.bodyEl.setHeight(newHeight);
26160         return newHeight;
26161     },
26162
26163     onResize : function(){
26164         if(this.monitorResize){
26165             this.autoSizeTabs();
26166         }
26167     },
26168
26169     /**
26170      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26171      */
26172     beginUpdate : function(){
26173         this.updating = true;
26174     },
26175
26176     /**
26177      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26178      */
26179     endUpdate : function(){
26180         this.updating = false;
26181         this.autoSizeTabs();
26182     },
26183
26184     /**
26185      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26186      */
26187     autoSizeTabs : function(){
26188         var count = this.items.length;
26189         var vcount = count - this.hiddenCount;
26190         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26191         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26192         var availWidth = Math.floor(w / vcount);
26193         var b = this.stripBody;
26194         if(b.getWidth() > w){
26195             var tabs = this.items;
26196             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26197             if(availWidth < this.minTabWidth){
26198                 /*if(!this.sleft){    // incomplete scrolling code
26199                     this.createScrollButtons();
26200                 }
26201                 this.showScroll();
26202                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26203             }
26204         }else{
26205             if(this.currentTabWidth < this.preferredTabWidth){
26206                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26207             }
26208         }
26209     },
26210
26211     /**
26212      * Returns the number of tabs in this TabPanel.
26213      * @return {Number}
26214      */
26215      getCount : function(){
26216          return this.items.length;
26217      },
26218
26219     /**
26220      * Resizes all the tabs to the passed width
26221      * @param {Number} The new width
26222      */
26223     setTabWidth : function(width){
26224         this.currentTabWidth = width;
26225         for(var i = 0, len = this.items.length; i < len; i++) {
26226                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26227         }
26228     },
26229
26230     /**
26231      * Destroys this TabPanel
26232      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26233      */
26234     destroy : function(removeEl){
26235         Roo.EventManager.removeResizeListener(this.onResize, this);
26236         for(var i = 0, len = this.items.length; i < len; i++){
26237             this.items[i].purgeListeners();
26238         }
26239         if(removeEl === true){
26240             this.el.update("");
26241             this.el.remove();
26242         }
26243     }
26244 });
26245
26246 /**
26247  * @class Roo.TabPanelItem
26248  * @extends Roo.util.Observable
26249  * Represents an individual item (tab plus body) in a TabPanel.
26250  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26251  * @param {String} id The id of this TabPanelItem
26252  * @param {String} text The text for the tab of this TabPanelItem
26253  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26254  */
26255 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26256     /**
26257      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26258      * @type Roo.TabPanel
26259      */
26260     this.tabPanel = tabPanel;
26261     /**
26262      * The id for this TabPanelItem
26263      * @type String
26264      */
26265     this.id = id;
26266     /** @private */
26267     this.disabled = false;
26268     /** @private */
26269     this.text = text;
26270     /** @private */
26271     this.loaded = false;
26272     this.closable = closable;
26273
26274     /**
26275      * The body element for this TabPanelItem.
26276      * @type Roo.Element
26277      */
26278     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26279     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26280     this.bodyEl.setStyle("display", "block");
26281     this.bodyEl.setStyle("zoom", "1");
26282     this.hideAction();
26283
26284     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26285     /** @private */
26286     this.el = Roo.get(els.el, true);
26287     this.inner = Roo.get(els.inner, true);
26288     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26289     this.pnode = Roo.get(els.el.parentNode, true);
26290     this.el.on("mousedown", this.onTabMouseDown, this);
26291     this.el.on("click", this.onTabClick, this);
26292     /** @private */
26293     if(closable){
26294         var c = Roo.get(els.close, true);
26295         c.dom.title = this.closeText;
26296         c.addClassOnOver("close-over");
26297         c.on("click", this.closeClick, this);
26298      }
26299
26300     this.addEvents({
26301          /**
26302          * @event activate
26303          * Fires when this tab becomes the active tab.
26304          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26305          * @param {Roo.TabPanelItem} this
26306          */
26307         "activate": true,
26308         /**
26309          * @event beforeclose
26310          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26311          * @param {Roo.TabPanelItem} this
26312          * @param {Object} e Set cancel to true on this object to cancel the close.
26313          */
26314         "beforeclose": true,
26315         /**
26316          * @event close
26317          * Fires when this tab is closed.
26318          * @param {Roo.TabPanelItem} this
26319          */
26320          "close": true,
26321         /**
26322          * @event deactivate
26323          * Fires when this tab is no longer the active tab.
26324          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26325          * @param {Roo.TabPanelItem} this
26326          */
26327          "deactivate" : true
26328     });
26329     this.hidden = false;
26330
26331     Roo.TabPanelItem.superclass.constructor.call(this);
26332 };
26333
26334 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26335     purgeListeners : function(){
26336        Roo.util.Observable.prototype.purgeListeners.call(this);
26337        this.el.removeAllListeners();
26338     },
26339     /**
26340      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26341      */
26342     show : function(){
26343         this.pnode.addClass("on");
26344         this.showAction();
26345         if(Roo.isOpera){
26346             this.tabPanel.stripWrap.repaint();
26347         }
26348         this.fireEvent("activate", this.tabPanel, this);
26349     },
26350
26351     /**
26352      * Returns true if this tab is the active tab.
26353      * @return {Boolean}
26354      */
26355     isActive : function(){
26356         return this.tabPanel.getActiveTab() == this;
26357     },
26358
26359     /**
26360      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
26361      */
26362     hide : function(){
26363         this.pnode.removeClass("on");
26364         this.hideAction();
26365         this.fireEvent("deactivate", this.tabPanel, this);
26366     },
26367
26368     hideAction : function(){
26369         this.bodyEl.hide();
26370         this.bodyEl.setStyle("position", "absolute");
26371         this.bodyEl.setLeft("-20000px");
26372         this.bodyEl.setTop("-20000px");
26373     },
26374
26375     showAction : function(){
26376         this.bodyEl.setStyle("position", "relative");
26377         this.bodyEl.setTop("");
26378         this.bodyEl.setLeft("");
26379         this.bodyEl.show();
26380     },
26381
26382     /**
26383      * Set the tooltip for the tab.
26384      * @param {String} tooltip The tab's tooltip
26385      */
26386     setTooltip : function(text){
26387         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
26388             this.textEl.dom.qtip = text;
26389             this.textEl.dom.removeAttribute('title');
26390         }else{
26391             this.textEl.dom.title = text;
26392         }
26393     },
26394
26395     onTabClick : function(e){
26396         e.preventDefault();
26397         this.tabPanel.activate(this.id);
26398     },
26399
26400     onTabMouseDown : function(e){
26401         e.preventDefault();
26402         this.tabPanel.activate(this.id);
26403     },
26404
26405     getWidth : function(){
26406         return this.inner.getWidth();
26407     },
26408
26409     setWidth : function(width){
26410         var iwidth = width - this.pnode.getPadding("lr");
26411         this.inner.setWidth(iwidth);
26412         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
26413         this.pnode.setWidth(width);
26414     },
26415
26416     /**
26417      * Show or hide the tab
26418      * @param {Boolean} hidden True to hide or false to show.
26419      */
26420     setHidden : function(hidden){
26421         this.hidden = hidden;
26422         this.pnode.setStyle("display", hidden ? "none" : "");
26423     },
26424
26425     /**
26426      * Returns true if this tab is "hidden"
26427      * @return {Boolean}
26428      */
26429     isHidden : function(){
26430         return this.hidden;
26431     },
26432
26433     /**
26434      * Returns the text for this tab
26435      * @return {String}
26436      */
26437     getText : function(){
26438         return this.text;
26439     },
26440
26441     autoSize : function(){
26442         //this.el.beginMeasure();
26443         this.textEl.setWidth(1);
26444         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
26445         //this.el.endMeasure();
26446     },
26447
26448     /**
26449      * Sets the text for the tab (Note: this also sets the tooltip text)
26450      * @param {String} text The tab's text and tooltip
26451      */
26452     setText : function(text){
26453         this.text = text;
26454         this.textEl.update(text);
26455         this.setTooltip(text);
26456         if(!this.tabPanel.resizeTabs){
26457             this.autoSize();
26458         }
26459     },
26460     /**
26461      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
26462      */
26463     activate : function(){
26464         this.tabPanel.activate(this.id);
26465     },
26466
26467     /**
26468      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
26469      */
26470     disable : function(){
26471         if(this.tabPanel.active != this){
26472             this.disabled = true;
26473             this.pnode.addClass("disabled");
26474         }
26475     },
26476
26477     /**
26478      * Enables this TabPanelItem if it was previously disabled.
26479      */
26480     enable : function(){
26481         this.disabled = false;
26482         this.pnode.removeClass("disabled");
26483     },
26484
26485     /**
26486      * Sets the content for this TabPanelItem.
26487      * @param {String} content The content
26488      * @param {Boolean} loadScripts true to look for and load scripts
26489      */
26490     setContent : function(content, loadScripts){
26491         this.bodyEl.update(content, loadScripts);
26492     },
26493
26494     /**
26495      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
26496      * @return {Roo.UpdateManager} The UpdateManager
26497      */
26498     getUpdateManager : function(){
26499         return this.bodyEl.getUpdateManager();
26500     },
26501
26502     /**
26503      * Set a URL to be used to load the content for this TabPanelItem.
26504      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
26505      * @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)
26506      * @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)
26507      * @return {Roo.UpdateManager} The UpdateManager
26508      */
26509     setUrl : function(url, params, loadOnce){
26510         if(this.refreshDelegate){
26511             this.un('activate', this.refreshDelegate);
26512         }
26513         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
26514         this.on("activate", this.refreshDelegate);
26515         return this.bodyEl.getUpdateManager();
26516     },
26517
26518     /** @private */
26519     _handleRefresh : function(url, params, loadOnce){
26520         if(!loadOnce || !this.loaded){
26521             var updater = this.bodyEl.getUpdateManager();
26522             updater.update(url, params, this._setLoaded.createDelegate(this));
26523         }
26524     },
26525
26526     /**
26527      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
26528      *   Will fail silently if the setUrl method has not been called.
26529      *   This does not activate the panel, just updates its content.
26530      */
26531     refresh : function(){
26532         if(this.refreshDelegate){
26533            this.loaded = false;
26534            this.refreshDelegate();
26535         }
26536     },
26537
26538     /** @private */
26539     _setLoaded : function(){
26540         this.loaded = true;
26541     },
26542
26543     /** @private */
26544     closeClick : function(e){
26545         var o = {};
26546         e.stopEvent();
26547         this.fireEvent("beforeclose", this, o);
26548         if(o.cancel !== true){
26549             this.tabPanel.removeTab(this.id);
26550         }
26551     },
26552     /**
26553      * The text displayed in the tooltip for the close icon.
26554      * @type String
26555      */
26556     closeText : "Close this tab"
26557 });
26558
26559 /** @private */
26560 Roo.TabPanel.prototype.createStrip = function(container){
26561     var strip = document.createElement("div");
26562     strip.className = "x-tabs-wrap";
26563     container.appendChild(strip);
26564     return strip;
26565 };
26566 /** @private */
26567 Roo.TabPanel.prototype.createStripList = function(strip){
26568     // div wrapper for retard IE
26569     // returns the "tr" element.
26570     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26571         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26572         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26573     return strip.firstChild.firstChild.firstChild.firstChild;
26574 };
26575 /** @private */
26576 Roo.TabPanel.prototype.createBody = function(container){
26577     var body = document.createElement("div");
26578     Roo.id(body, "tab-body");
26579     Roo.fly(body).addClass("x-tabs-body");
26580     container.appendChild(body);
26581     return body;
26582 };
26583 /** @private */
26584 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26585     var body = Roo.getDom(id);
26586     if(!body){
26587         body = document.createElement("div");
26588         body.id = id;
26589     }
26590     Roo.fly(body).addClass("x-tabs-item-body");
26591     bodyEl.insertBefore(body, bodyEl.firstChild);
26592     return body;
26593 };
26594 /** @private */
26595 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26596     var td = document.createElement("td");
26597     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26598     //stripEl.appendChild(td);
26599     if(closable){
26600         td.className = "x-tabs-closable";
26601         if(!this.closeTpl){
26602             this.closeTpl = new Roo.Template(
26603                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26604                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26605                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26606             );
26607         }
26608         var el = this.closeTpl.overwrite(td, {"text": text});
26609         var close = el.getElementsByTagName("div")[0];
26610         var inner = el.getElementsByTagName("em")[0];
26611         return {"el": el, "close": close, "inner": inner};
26612     } else {
26613         if(!this.tabTpl){
26614             this.tabTpl = new Roo.Template(
26615                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26616                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26617             );
26618         }
26619         var el = this.tabTpl.overwrite(td, {"text": text});
26620         var inner = el.getElementsByTagName("em")[0];
26621         return {"el": el, "inner": inner};
26622     }
26623 };/*
26624  * Based on:
26625  * Ext JS Library 1.1.1
26626  * Copyright(c) 2006-2007, Ext JS, LLC.
26627  *
26628  * Originally Released Under LGPL - original licence link has changed is not relivant.
26629  *
26630  * Fork - LGPL
26631  * <script type="text/javascript">
26632  */
26633
26634 /**
26635  * @class Roo.Button
26636  * @extends Roo.util.Observable
26637  * Simple Button class
26638  * @cfg {String} text The button text
26639  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26640  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26641  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26642  * @cfg {Object} scope The scope of the handler
26643  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26644  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26645  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26646  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26647  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26648  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26649    applies if enableToggle = true)
26650  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26651  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26652   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26653  * @constructor
26654  * Create a new button
26655  * @param {Object} config The config object
26656  */
26657 Roo.Button = function(renderTo, config)
26658 {
26659     if (!config) {
26660         config = renderTo;
26661         renderTo = config.renderTo || false;
26662     }
26663     
26664     Roo.apply(this, config);
26665     this.addEvents({
26666         /**
26667              * @event click
26668              * Fires when this button is clicked
26669              * @param {Button} this
26670              * @param {EventObject} e The click event
26671              */
26672             "click" : true,
26673         /**
26674              * @event toggle
26675              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26676              * @param {Button} this
26677              * @param {Boolean} pressed
26678              */
26679             "toggle" : true,
26680         /**
26681              * @event mouseover
26682              * Fires when the mouse hovers over the button
26683              * @param {Button} this
26684              * @param {Event} e The event object
26685              */
26686         'mouseover' : true,
26687         /**
26688              * @event mouseout
26689              * Fires when the mouse exits the button
26690              * @param {Button} this
26691              * @param {Event} e The event object
26692              */
26693         'mouseout': true,
26694          /**
26695              * @event render
26696              * Fires when the button is rendered
26697              * @param {Button} this
26698              */
26699         'render': true
26700     });
26701     if(this.menu){
26702         this.menu = Roo.menu.MenuMgr.get(this.menu);
26703     }
26704     // register listeners first!!  - so render can be captured..
26705     Roo.util.Observable.call(this);
26706     if(renderTo){
26707         this.render(renderTo);
26708     }
26709     
26710   
26711 };
26712
26713 Roo.extend(Roo.Button, Roo.util.Observable, {
26714     /**
26715      * 
26716      */
26717     
26718     /**
26719      * Read-only. True if this button is hidden
26720      * @type Boolean
26721      */
26722     hidden : false,
26723     /**
26724      * Read-only. True if this button is disabled
26725      * @type Boolean
26726      */
26727     disabled : false,
26728     /**
26729      * Read-only. True if this button is pressed (only if enableToggle = true)
26730      * @type Boolean
26731      */
26732     pressed : false,
26733
26734     /**
26735      * @cfg {Number} tabIndex 
26736      * The DOM tabIndex for this button (defaults to undefined)
26737      */
26738     tabIndex : undefined,
26739
26740     /**
26741      * @cfg {Boolean} enableToggle
26742      * True to enable pressed/not pressed toggling (defaults to false)
26743      */
26744     enableToggle: false,
26745     /**
26746      * @cfg {Mixed} menu
26747      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26748      */
26749     menu : undefined,
26750     /**
26751      * @cfg {String} menuAlign
26752      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26753      */
26754     menuAlign : "tl-bl?",
26755
26756     /**
26757      * @cfg {String} iconCls
26758      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26759      */
26760     iconCls : undefined,
26761     /**
26762      * @cfg {String} type
26763      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26764      */
26765     type : 'button',
26766
26767     // private
26768     menuClassTarget: 'tr',
26769
26770     /**
26771      * @cfg {String} clickEvent
26772      * The type of event to map to the button's event handler (defaults to 'click')
26773      */
26774     clickEvent : 'click',
26775
26776     /**
26777      * @cfg {Boolean} handleMouseEvents
26778      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26779      */
26780     handleMouseEvents : true,
26781
26782     /**
26783      * @cfg {String} tooltipType
26784      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26785      */
26786     tooltipType : 'qtip',
26787
26788     /**
26789      * @cfg {String} cls
26790      * A CSS class to apply to the button's main element.
26791      */
26792     
26793     /**
26794      * @cfg {Roo.Template} template (Optional)
26795      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26796      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26797      * require code modifications if required elements (e.g. a button) aren't present.
26798      */
26799
26800     // private
26801     render : function(renderTo){
26802         var btn;
26803         if(this.hideParent){
26804             this.parentEl = Roo.get(renderTo);
26805         }
26806         if(!this.dhconfig){
26807             if(!this.template){
26808                 if(!Roo.Button.buttonTemplate){
26809                     // hideous table template
26810                     Roo.Button.buttonTemplate = new Roo.Template(
26811                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26812                         '<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>',
26813                         "</tr></tbody></table>");
26814                 }
26815                 this.template = Roo.Button.buttonTemplate;
26816             }
26817             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26818             var btnEl = btn.child("button:first");
26819             btnEl.on('focus', this.onFocus, this);
26820             btnEl.on('blur', this.onBlur, this);
26821             if(this.cls){
26822                 btn.addClass(this.cls);
26823             }
26824             if(this.icon){
26825                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26826             }
26827             if(this.iconCls){
26828                 btnEl.addClass(this.iconCls);
26829                 if(!this.cls){
26830                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26831                 }
26832             }
26833             if(this.tabIndex !== undefined){
26834                 btnEl.dom.tabIndex = this.tabIndex;
26835             }
26836             if(this.tooltip){
26837                 if(typeof this.tooltip == 'object'){
26838                     Roo.QuickTips.tips(Roo.apply({
26839                           target: btnEl.id
26840                     }, this.tooltip));
26841                 } else {
26842                     btnEl.dom[this.tooltipType] = this.tooltip;
26843                 }
26844             }
26845         }else{
26846             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26847         }
26848         this.el = btn;
26849         if(this.id){
26850             this.el.dom.id = this.el.id = this.id;
26851         }
26852         if(this.menu){
26853             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26854             this.menu.on("show", this.onMenuShow, this);
26855             this.menu.on("hide", this.onMenuHide, this);
26856         }
26857         btn.addClass("x-btn");
26858         if(Roo.isIE && !Roo.isIE7){
26859             this.autoWidth.defer(1, this);
26860         }else{
26861             this.autoWidth();
26862         }
26863         if(this.handleMouseEvents){
26864             btn.on("mouseover", this.onMouseOver, this);
26865             btn.on("mouseout", this.onMouseOut, this);
26866             btn.on("mousedown", this.onMouseDown, this);
26867         }
26868         btn.on(this.clickEvent, this.onClick, this);
26869         //btn.on("mouseup", this.onMouseUp, this);
26870         if(this.hidden){
26871             this.hide();
26872         }
26873         if(this.disabled){
26874             this.disable();
26875         }
26876         Roo.ButtonToggleMgr.register(this);
26877         if(this.pressed){
26878             this.el.addClass("x-btn-pressed");
26879         }
26880         if(this.repeat){
26881             var repeater = new Roo.util.ClickRepeater(btn,
26882                 typeof this.repeat == "object" ? this.repeat : {}
26883             );
26884             repeater.on("click", this.onClick,  this);
26885         }
26886         
26887         this.fireEvent('render', this);
26888         
26889     },
26890     /**
26891      * Returns the button's underlying element
26892      * @return {Roo.Element} The element
26893      */
26894     getEl : function(){
26895         return this.el;  
26896     },
26897     
26898     /**
26899      * Destroys this Button and removes any listeners.
26900      */
26901     destroy : function(){
26902         Roo.ButtonToggleMgr.unregister(this);
26903         this.el.removeAllListeners();
26904         this.purgeListeners();
26905         this.el.remove();
26906     },
26907
26908     // private
26909     autoWidth : function(){
26910         if(this.el){
26911             this.el.setWidth("auto");
26912             if(Roo.isIE7 && Roo.isStrict){
26913                 var ib = this.el.child('button');
26914                 if(ib && ib.getWidth() > 20){
26915                     ib.clip();
26916                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26917                 }
26918             }
26919             if(this.minWidth){
26920                 if(this.hidden){
26921                     this.el.beginMeasure();
26922                 }
26923                 if(this.el.getWidth() < this.minWidth){
26924                     this.el.setWidth(this.minWidth);
26925                 }
26926                 if(this.hidden){
26927                     this.el.endMeasure();
26928                 }
26929             }
26930         }
26931     },
26932
26933     /**
26934      * Assigns this button's click handler
26935      * @param {Function} handler The function to call when the button is clicked
26936      * @param {Object} scope (optional) Scope for the function passed in
26937      */
26938     setHandler : function(handler, scope){
26939         this.handler = handler;
26940         this.scope = scope;  
26941     },
26942     
26943     /**
26944      * Sets this button's text
26945      * @param {String} text The button text
26946      */
26947     setText : function(text){
26948         this.text = text;
26949         if(this.el){
26950             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26951         }
26952         this.autoWidth();
26953     },
26954     
26955     /**
26956      * Gets the text for this button
26957      * @return {String} The button text
26958      */
26959     getText : function(){
26960         return this.text;  
26961     },
26962     
26963     /**
26964      * Show this button
26965      */
26966     show: function(){
26967         this.hidden = false;
26968         if(this.el){
26969             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26970         }
26971     },
26972     
26973     /**
26974      * Hide this button
26975      */
26976     hide: function(){
26977         this.hidden = true;
26978         if(this.el){
26979             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26980         }
26981     },
26982     
26983     /**
26984      * Convenience function for boolean show/hide
26985      * @param {Boolean} visible True to show, false to hide
26986      */
26987     setVisible: function(visible){
26988         if(visible) {
26989             this.show();
26990         }else{
26991             this.hide();
26992         }
26993     },
26994     
26995     /**
26996      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26997      * @param {Boolean} state (optional) Force a particular state
26998      */
26999     toggle : function(state){
27000         state = state === undefined ? !this.pressed : state;
27001         if(state != this.pressed){
27002             if(state){
27003                 this.el.addClass("x-btn-pressed");
27004                 this.pressed = true;
27005                 this.fireEvent("toggle", this, true);
27006             }else{
27007                 this.el.removeClass("x-btn-pressed");
27008                 this.pressed = false;
27009                 this.fireEvent("toggle", this, false);
27010             }
27011             if(this.toggleHandler){
27012                 this.toggleHandler.call(this.scope || this, this, state);
27013             }
27014         }
27015     },
27016     
27017     /**
27018      * Focus the button
27019      */
27020     focus : function(){
27021         this.el.child('button:first').focus();
27022     },
27023     
27024     /**
27025      * Disable this button
27026      */
27027     disable : function(){
27028         if(this.el){
27029             this.el.addClass("x-btn-disabled");
27030         }
27031         this.disabled = true;
27032     },
27033     
27034     /**
27035      * Enable this button
27036      */
27037     enable : function(){
27038         if(this.el){
27039             this.el.removeClass("x-btn-disabled");
27040         }
27041         this.disabled = false;
27042     },
27043
27044     /**
27045      * Convenience function for boolean enable/disable
27046      * @param {Boolean} enabled True to enable, false to disable
27047      */
27048     setDisabled : function(v){
27049         this[v !== true ? "enable" : "disable"]();
27050     },
27051
27052     // private
27053     onClick : function(e){
27054         if(e){
27055             e.preventDefault();
27056         }
27057         if(e.button != 0){
27058             return;
27059         }
27060         if(!this.disabled){
27061             if(this.enableToggle){
27062                 this.toggle();
27063             }
27064             if(this.menu && !this.menu.isVisible()){
27065                 this.menu.show(this.el, this.menuAlign);
27066             }
27067             this.fireEvent("click", this, e);
27068             if(this.handler){
27069                 this.el.removeClass("x-btn-over");
27070                 this.handler.call(this.scope || this, this, e);
27071             }
27072         }
27073     },
27074     // private
27075     onMouseOver : function(e){
27076         if(!this.disabled){
27077             this.el.addClass("x-btn-over");
27078             this.fireEvent('mouseover', this, e);
27079         }
27080     },
27081     // private
27082     onMouseOut : function(e){
27083         if(!e.within(this.el,  true)){
27084             this.el.removeClass("x-btn-over");
27085             this.fireEvent('mouseout', this, e);
27086         }
27087     },
27088     // private
27089     onFocus : function(e){
27090         if(!this.disabled){
27091             this.el.addClass("x-btn-focus");
27092         }
27093     },
27094     // private
27095     onBlur : function(e){
27096         this.el.removeClass("x-btn-focus");
27097     },
27098     // private
27099     onMouseDown : function(e){
27100         if(!this.disabled && e.button == 0){
27101             this.el.addClass("x-btn-click");
27102             Roo.get(document).on('mouseup', this.onMouseUp, this);
27103         }
27104     },
27105     // private
27106     onMouseUp : function(e){
27107         if(e.button == 0){
27108             this.el.removeClass("x-btn-click");
27109             Roo.get(document).un('mouseup', this.onMouseUp, this);
27110         }
27111     },
27112     // private
27113     onMenuShow : function(e){
27114         this.el.addClass("x-btn-menu-active");
27115     },
27116     // private
27117     onMenuHide : function(e){
27118         this.el.removeClass("x-btn-menu-active");
27119     }   
27120 });
27121
27122 // Private utility class used by Button
27123 Roo.ButtonToggleMgr = function(){
27124    var groups = {};
27125    
27126    function toggleGroup(btn, state){
27127        if(state){
27128            var g = groups[btn.toggleGroup];
27129            for(var i = 0, l = g.length; i < l; i++){
27130                if(g[i] != btn){
27131                    g[i].toggle(false);
27132                }
27133            }
27134        }
27135    }
27136    
27137    return {
27138        register : function(btn){
27139            if(!btn.toggleGroup){
27140                return;
27141            }
27142            var g = groups[btn.toggleGroup];
27143            if(!g){
27144                g = groups[btn.toggleGroup] = [];
27145            }
27146            g.push(btn);
27147            btn.on("toggle", toggleGroup);
27148        },
27149        
27150        unregister : function(btn){
27151            if(!btn.toggleGroup){
27152                return;
27153            }
27154            var g = groups[btn.toggleGroup];
27155            if(g){
27156                g.remove(btn);
27157                btn.un("toggle", toggleGroup);
27158            }
27159        }
27160    };
27161 }();/*
27162  * Based on:
27163  * Ext JS Library 1.1.1
27164  * Copyright(c) 2006-2007, Ext JS, LLC.
27165  *
27166  * Originally Released Under LGPL - original licence link has changed is not relivant.
27167  *
27168  * Fork - LGPL
27169  * <script type="text/javascript">
27170  */
27171  
27172 /**
27173  * @class Roo.SplitButton
27174  * @extends Roo.Button
27175  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27176  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27177  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27178  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27179  * @cfg {String} arrowTooltip The title attribute of the arrow
27180  * @constructor
27181  * Create a new menu button
27182  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27183  * @param {Object} config The config object
27184  */
27185 Roo.SplitButton = function(renderTo, config){
27186     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27187     /**
27188      * @event arrowclick
27189      * Fires when this button's arrow is clicked
27190      * @param {SplitButton} this
27191      * @param {EventObject} e The click event
27192      */
27193     this.addEvents({"arrowclick":true});
27194 };
27195
27196 Roo.extend(Roo.SplitButton, Roo.Button, {
27197     render : function(renderTo){
27198         // this is one sweet looking template!
27199         var tpl = new Roo.Template(
27200             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27201             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27202             '<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>',
27203             "</tbody></table></td><td>",
27204             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27205             '<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>',
27206             "</tbody></table></td></tr></table>"
27207         );
27208         var btn = tpl.append(renderTo, [this.text, this.type], true);
27209         var btnEl = btn.child("button");
27210         if(this.cls){
27211             btn.addClass(this.cls);
27212         }
27213         if(this.icon){
27214             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27215         }
27216         if(this.iconCls){
27217             btnEl.addClass(this.iconCls);
27218             if(!this.cls){
27219                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27220             }
27221         }
27222         this.el = btn;
27223         if(this.handleMouseEvents){
27224             btn.on("mouseover", this.onMouseOver, this);
27225             btn.on("mouseout", this.onMouseOut, this);
27226             btn.on("mousedown", this.onMouseDown, this);
27227             btn.on("mouseup", this.onMouseUp, this);
27228         }
27229         btn.on(this.clickEvent, this.onClick, this);
27230         if(this.tooltip){
27231             if(typeof this.tooltip == 'object'){
27232                 Roo.QuickTips.tips(Roo.apply({
27233                       target: btnEl.id
27234                 }, this.tooltip));
27235             } else {
27236                 btnEl.dom[this.tooltipType] = this.tooltip;
27237             }
27238         }
27239         if(this.arrowTooltip){
27240             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27241         }
27242         if(this.hidden){
27243             this.hide();
27244         }
27245         if(this.disabled){
27246             this.disable();
27247         }
27248         if(this.pressed){
27249             this.el.addClass("x-btn-pressed");
27250         }
27251         if(Roo.isIE && !Roo.isIE7){
27252             this.autoWidth.defer(1, this);
27253         }else{
27254             this.autoWidth();
27255         }
27256         if(this.menu){
27257             this.menu.on("show", this.onMenuShow, this);
27258             this.menu.on("hide", this.onMenuHide, this);
27259         }
27260         this.fireEvent('render', this);
27261     },
27262
27263     // private
27264     autoWidth : function(){
27265         if(this.el){
27266             var tbl = this.el.child("table:first");
27267             var tbl2 = this.el.child("table:last");
27268             this.el.setWidth("auto");
27269             tbl.setWidth("auto");
27270             if(Roo.isIE7 && Roo.isStrict){
27271                 var ib = this.el.child('button:first');
27272                 if(ib && ib.getWidth() > 20){
27273                     ib.clip();
27274                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27275                 }
27276             }
27277             if(this.minWidth){
27278                 if(this.hidden){
27279                     this.el.beginMeasure();
27280                 }
27281                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27282                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27283                 }
27284                 if(this.hidden){
27285                     this.el.endMeasure();
27286                 }
27287             }
27288             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27289         } 
27290     },
27291     /**
27292      * Sets this button's click handler
27293      * @param {Function} handler The function to call when the button is clicked
27294      * @param {Object} scope (optional) Scope for the function passed above
27295      */
27296     setHandler : function(handler, scope){
27297         this.handler = handler;
27298         this.scope = scope;  
27299     },
27300     
27301     /**
27302      * Sets this button's arrow click handler
27303      * @param {Function} handler The function to call when the arrow is clicked
27304      * @param {Object} scope (optional) Scope for the function passed above
27305      */
27306     setArrowHandler : function(handler, scope){
27307         this.arrowHandler = handler;
27308         this.scope = scope;  
27309     },
27310     
27311     /**
27312      * Focus the button
27313      */
27314     focus : function(){
27315         if(this.el){
27316             this.el.child("button:first").focus();
27317         }
27318     },
27319
27320     // private
27321     onClick : function(e){
27322         e.preventDefault();
27323         if(!this.disabled){
27324             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27325                 if(this.menu && !this.menu.isVisible()){
27326                     this.menu.show(this.el, this.menuAlign);
27327                 }
27328                 this.fireEvent("arrowclick", this, e);
27329                 if(this.arrowHandler){
27330                     this.arrowHandler.call(this.scope || this, this, e);
27331                 }
27332             }else{
27333                 this.fireEvent("click", this, e);
27334                 if(this.handler){
27335                     this.handler.call(this.scope || this, this, e);
27336                 }
27337             }
27338         }
27339     },
27340     // private
27341     onMouseDown : function(e){
27342         if(!this.disabled){
27343             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27344         }
27345     },
27346     // private
27347     onMouseUp : function(e){
27348         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27349     }   
27350 });
27351
27352
27353 // backwards compat
27354 Roo.MenuButton = Roo.SplitButton;/*
27355  * Based on:
27356  * Ext JS Library 1.1.1
27357  * Copyright(c) 2006-2007, Ext JS, LLC.
27358  *
27359  * Originally Released Under LGPL - original licence link has changed is not relivant.
27360  *
27361  * Fork - LGPL
27362  * <script type="text/javascript">
27363  */
27364
27365 /**
27366  * @class Roo.Toolbar
27367  * Basic Toolbar class.
27368  * @constructor
27369  * Creates a new Toolbar
27370  * @param {Object} container The config object
27371  */ 
27372 Roo.Toolbar = function(container, buttons, config)
27373 {
27374     /// old consturctor format still supported..
27375     if(container instanceof Array){ // omit the container for later rendering
27376         buttons = container;
27377         config = buttons;
27378         container = null;
27379     }
27380     if (typeof(container) == 'object' && container.xtype) {
27381         config = container;
27382         container = config.container;
27383         buttons = config.buttons || []; // not really - use items!!
27384     }
27385     var xitems = [];
27386     if (config && config.items) {
27387         xitems = config.items;
27388         delete config.items;
27389     }
27390     Roo.apply(this, config);
27391     this.buttons = buttons;
27392     
27393     if(container){
27394         this.render(container);
27395     }
27396     this.xitems = xitems;
27397     Roo.each(xitems, function(b) {
27398         this.add(b);
27399     }, this);
27400     
27401 };
27402
27403 Roo.Toolbar.prototype = {
27404     /**
27405      * @cfg {Array} items
27406      * array of button configs or elements to add (will be converted to a MixedCollection)
27407      */
27408     
27409     /**
27410      * @cfg {String/HTMLElement/Element} container
27411      * The id or element that will contain the toolbar
27412      */
27413     // private
27414     render : function(ct){
27415         this.el = Roo.get(ct);
27416         if(this.cls){
27417             this.el.addClass(this.cls);
27418         }
27419         // using a table allows for vertical alignment
27420         // 100% width is needed by Safari...
27421         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
27422         this.tr = this.el.child("tr", true);
27423         var autoId = 0;
27424         this.items = new Roo.util.MixedCollection(false, function(o){
27425             return o.id || ("item" + (++autoId));
27426         });
27427         if(this.buttons){
27428             this.add.apply(this, this.buttons);
27429             delete this.buttons;
27430         }
27431     },
27432
27433     /**
27434      * Adds element(s) to the toolbar -- this function takes a variable number of 
27435      * arguments of mixed type and adds them to the toolbar.
27436      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
27437      * <ul>
27438      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
27439      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
27440      * <li>Field: Any form field (equivalent to {@link #addField})</li>
27441      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
27442      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
27443      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
27444      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
27445      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
27446      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
27447      * </ul>
27448      * @param {Mixed} arg2
27449      * @param {Mixed} etc.
27450      */
27451     add : function(){
27452         var a = arguments, l = a.length;
27453         for(var i = 0; i < l; i++){
27454             this._add(a[i]);
27455         }
27456     },
27457     // private..
27458     _add : function(el) {
27459         
27460         if (el.xtype) {
27461             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
27462         }
27463         
27464         if (el.applyTo){ // some kind of form field
27465             return this.addField(el);
27466         } 
27467         if (el.render){ // some kind of Toolbar.Item
27468             return this.addItem(el);
27469         }
27470         if (typeof el == "string"){ // string
27471             if(el == "separator" || el == "-"){
27472                 return this.addSeparator();
27473             }
27474             if (el == " "){
27475                 return this.addSpacer();
27476             }
27477             if(el == "->"){
27478                 return this.addFill();
27479             }
27480             return this.addText(el);
27481             
27482         }
27483         if(el.tagName){ // element
27484             return this.addElement(el);
27485         }
27486         if(typeof el == "object"){ // must be button config?
27487             return this.addButton(el);
27488         }
27489         // and now what?!?!
27490         return false;
27491         
27492     },
27493     
27494     /**
27495      * Add an Xtype element
27496      * @param {Object} xtype Xtype Object
27497      * @return {Object} created Object
27498      */
27499     addxtype : function(e){
27500         return this.add(e);  
27501     },
27502     
27503     /**
27504      * Returns the Element for this toolbar.
27505      * @return {Roo.Element}
27506      */
27507     getEl : function(){
27508         return this.el;  
27509     },
27510     
27511     /**
27512      * Adds a separator
27513      * @return {Roo.Toolbar.Item} The separator item
27514      */
27515     addSeparator : function(){
27516         return this.addItem(new Roo.Toolbar.Separator());
27517     },
27518
27519     /**
27520      * Adds a spacer element
27521      * @return {Roo.Toolbar.Spacer} The spacer item
27522      */
27523     addSpacer : function(){
27524         return this.addItem(new Roo.Toolbar.Spacer());
27525     },
27526
27527     /**
27528      * Adds a fill element that forces subsequent additions to the right side of the toolbar
27529      * @return {Roo.Toolbar.Fill} The fill item
27530      */
27531     addFill : function(){
27532         return this.addItem(new Roo.Toolbar.Fill());
27533     },
27534
27535     /**
27536      * Adds any standard HTML element to the toolbar
27537      * @param {String/HTMLElement/Element} el The element or id of the element to add
27538      * @return {Roo.Toolbar.Item} The element's item
27539      */
27540     addElement : function(el){
27541         return this.addItem(new Roo.Toolbar.Item(el));
27542     },
27543     /**
27544      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
27545      * @type Roo.util.MixedCollection  
27546      */
27547     items : false,
27548      
27549     /**
27550      * Adds any Toolbar.Item or subclass
27551      * @param {Roo.Toolbar.Item} item
27552      * @return {Roo.Toolbar.Item} The item
27553      */
27554     addItem : function(item){
27555         var td = this.nextBlock();
27556         item.render(td);
27557         this.items.add(item);
27558         return item;
27559     },
27560     
27561     /**
27562      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
27563      * @param {Object/Array} config A button config or array of configs
27564      * @return {Roo.Toolbar.Button/Array}
27565      */
27566     addButton : function(config){
27567         if(config instanceof Array){
27568             var buttons = [];
27569             for(var i = 0, len = config.length; i < len; i++) {
27570                 buttons.push(this.addButton(config[i]));
27571             }
27572             return buttons;
27573         }
27574         var b = config;
27575         if(!(config instanceof Roo.Toolbar.Button)){
27576             b = config.split ?
27577                 new Roo.Toolbar.SplitButton(config) :
27578                 new Roo.Toolbar.Button(config);
27579         }
27580         var td = this.nextBlock();
27581         b.render(td);
27582         this.items.add(b);
27583         return b;
27584     },
27585     
27586     /**
27587      * Adds text to the toolbar
27588      * @param {String} text The text to add
27589      * @return {Roo.Toolbar.Item} The element's item
27590      */
27591     addText : function(text){
27592         return this.addItem(new Roo.Toolbar.TextItem(text));
27593     },
27594     
27595     /**
27596      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27597      * @param {Number} index The index where the item is to be inserted
27598      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27599      * @return {Roo.Toolbar.Button/Item}
27600      */
27601     insertButton : function(index, item){
27602         if(item instanceof Array){
27603             var buttons = [];
27604             for(var i = 0, len = item.length; i < len; i++) {
27605                buttons.push(this.insertButton(index + i, item[i]));
27606             }
27607             return buttons;
27608         }
27609         if (!(item instanceof Roo.Toolbar.Button)){
27610            item = new Roo.Toolbar.Button(item);
27611         }
27612         var td = document.createElement("td");
27613         this.tr.insertBefore(td, this.tr.childNodes[index]);
27614         item.render(td);
27615         this.items.insert(index, item);
27616         return item;
27617     },
27618     
27619     /**
27620      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27621      * @param {Object} config
27622      * @return {Roo.Toolbar.Item} The element's item
27623      */
27624     addDom : function(config, returnEl){
27625         var td = this.nextBlock();
27626         Roo.DomHelper.overwrite(td, config);
27627         var ti = new Roo.Toolbar.Item(td.firstChild);
27628         ti.render(td);
27629         this.items.add(ti);
27630         return ti;
27631     },
27632
27633     /**
27634      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27635      * @type Roo.util.MixedCollection  
27636      */
27637     fields : false,
27638     
27639     /**
27640      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27641      * Note: the field should not have been rendered yet. For a field that has already been
27642      * rendered, use {@link #addElement}.
27643      * @param {Roo.form.Field} field
27644      * @return {Roo.ToolbarItem}
27645      */
27646      
27647       
27648     addField : function(field) {
27649         if (!this.fields) {
27650             var autoId = 0;
27651             this.fields = new Roo.util.MixedCollection(false, function(o){
27652                 return o.id || ("item" + (++autoId));
27653             });
27654
27655         }
27656         
27657         var td = this.nextBlock();
27658         field.render(td);
27659         var ti = new Roo.Toolbar.Item(td.firstChild);
27660         ti.render(td);
27661         this.items.add(ti);
27662         this.fields.add(field);
27663         return ti;
27664     },
27665     /**
27666      * Hide the toolbar
27667      * @method hide
27668      */
27669      
27670       
27671     hide : function()
27672     {
27673         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27674         this.el.child('div').hide();
27675     },
27676     /**
27677      * Show the toolbar
27678      * @method show
27679      */
27680     show : function()
27681     {
27682         this.el.child('div').show();
27683     },
27684       
27685     // private
27686     nextBlock : function(){
27687         var td = document.createElement("td");
27688         this.tr.appendChild(td);
27689         return td;
27690     },
27691
27692     // private
27693     destroy : function(){
27694         if(this.items){ // rendered?
27695             Roo.destroy.apply(Roo, this.items.items);
27696         }
27697         if(this.fields){ // rendered?
27698             Roo.destroy.apply(Roo, this.fields.items);
27699         }
27700         Roo.Element.uncache(this.el, this.tr);
27701     }
27702 };
27703
27704 /**
27705  * @class Roo.Toolbar.Item
27706  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27707  * @constructor
27708  * Creates a new Item
27709  * @param {HTMLElement} el 
27710  */
27711 Roo.Toolbar.Item = function(el){
27712     this.el = Roo.getDom(el);
27713     this.id = Roo.id(this.el);
27714     this.hidden = false;
27715 };
27716
27717 Roo.Toolbar.Item.prototype = {
27718     
27719     /**
27720      * Get this item's HTML Element
27721      * @return {HTMLElement}
27722      */
27723     getEl : function(){
27724        return this.el;  
27725     },
27726
27727     // private
27728     render : function(td){
27729         this.td = td;
27730         td.appendChild(this.el);
27731     },
27732     
27733     /**
27734      * Removes and destroys this item.
27735      */
27736     destroy : function(){
27737         this.td.parentNode.removeChild(this.td);
27738     },
27739     
27740     /**
27741      * Shows this item.
27742      */
27743     show: function(){
27744         this.hidden = false;
27745         this.td.style.display = "";
27746     },
27747     
27748     /**
27749      * Hides this item.
27750      */
27751     hide: function(){
27752         this.hidden = true;
27753         this.td.style.display = "none";
27754     },
27755     
27756     /**
27757      * Convenience function for boolean show/hide.
27758      * @param {Boolean} visible true to show/false to hide
27759      */
27760     setVisible: function(visible){
27761         if(visible) {
27762             this.show();
27763         }else{
27764             this.hide();
27765         }
27766     },
27767     
27768     /**
27769      * Try to focus this item.
27770      */
27771     focus : function(){
27772         Roo.fly(this.el).focus();
27773     },
27774     
27775     /**
27776      * Disables this item.
27777      */
27778     disable : function(){
27779         Roo.fly(this.td).addClass("x-item-disabled");
27780         this.disabled = true;
27781         this.el.disabled = true;
27782     },
27783     
27784     /**
27785      * Enables this item.
27786      */
27787     enable : function(){
27788         Roo.fly(this.td).removeClass("x-item-disabled");
27789         this.disabled = false;
27790         this.el.disabled = false;
27791     }
27792 };
27793
27794
27795 /**
27796  * @class Roo.Toolbar.Separator
27797  * @extends Roo.Toolbar.Item
27798  * A simple toolbar separator class
27799  * @constructor
27800  * Creates a new Separator
27801  */
27802 Roo.Toolbar.Separator = function(){
27803     var s = document.createElement("span");
27804     s.className = "ytb-sep";
27805     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27806 };
27807 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27808     enable:Roo.emptyFn,
27809     disable:Roo.emptyFn,
27810     focus:Roo.emptyFn
27811 });
27812
27813 /**
27814  * @class Roo.Toolbar.Spacer
27815  * @extends Roo.Toolbar.Item
27816  * A simple element that adds extra horizontal space to a toolbar.
27817  * @constructor
27818  * Creates a new Spacer
27819  */
27820 Roo.Toolbar.Spacer = function(){
27821     var s = document.createElement("div");
27822     s.className = "ytb-spacer";
27823     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27824 };
27825 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27826     enable:Roo.emptyFn,
27827     disable:Roo.emptyFn,
27828     focus:Roo.emptyFn
27829 });
27830
27831 /**
27832  * @class Roo.Toolbar.Fill
27833  * @extends Roo.Toolbar.Spacer
27834  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27835  * @constructor
27836  * Creates a new Spacer
27837  */
27838 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27839     // private
27840     render : function(td){
27841         td.style.width = '100%';
27842         Roo.Toolbar.Fill.superclass.render.call(this, td);
27843     }
27844 });
27845
27846 /**
27847  * @class Roo.Toolbar.TextItem
27848  * @extends Roo.Toolbar.Item
27849  * A simple class that renders text directly into a toolbar.
27850  * @constructor
27851  * Creates a new TextItem
27852  * @param {String} text
27853  */
27854 Roo.Toolbar.TextItem = function(text){
27855     if (typeof(text) == 'object') {
27856         text = text.text;
27857     }
27858     var s = document.createElement("span");
27859     s.className = "ytb-text";
27860     s.innerHTML = text;
27861     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27862 };
27863 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27864     enable:Roo.emptyFn,
27865     disable:Roo.emptyFn,
27866     focus:Roo.emptyFn
27867 });
27868
27869 /**
27870  * @class Roo.Toolbar.Button
27871  * @extends Roo.Button
27872  * A button that renders into a toolbar.
27873  * @constructor
27874  * Creates a new Button
27875  * @param {Object} config A standard {@link Roo.Button} config object
27876  */
27877 Roo.Toolbar.Button = function(config){
27878     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27879 };
27880 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27881     render : function(td){
27882         this.td = td;
27883         Roo.Toolbar.Button.superclass.render.call(this, td);
27884     },
27885     
27886     /**
27887      * Removes and destroys this button
27888      */
27889     destroy : function(){
27890         Roo.Toolbar.Button.superclass.destroy.call(this);
27891         this.td.parentNode.removeChild(this.td);
27892     },
27893     
27894     /**
27895      * Shows this button
27896      */
27897     show: function(){
27898         this.hidden = false;
27899         this.td.style.display = "";
27900     },
27901     
27902     /**
27903      * Hides this button
27904      */
27905     hide: function(){
27906         this.hidden = true;
27907         this.td.style.display = "none";
27908     },
27909
27910     /**
27911      * Disables this item
27912      */
27913     disable : function(){
27914         Roo.fly(this.td).addClass("x-item-disabled");
27915         this.disabled = true;
27916     },
27917
27918     /**
27919      * Enables this item
27920      */
27921     enable : function(){
27922         Roo.fly(this.td).removeClass("x-item-disabled");
27923         this.disabled = false;
27924     }
27925 });
27926 // backwards compat
27927 Roo.ToolbarButton = Roo.Toolbar.Button;
27928
27929 /**
27930  * @class Roo.Toolbar.SplitButton
27931  * @extends Roo.SplitButton
27932  * A menu button that renders into a toolbar.
27933  * @constructor
27934  * Creates a new SplitButton
27935  * @param {Object} config A standard {@link Roo.SplitButton} config object
27936  */
27937 Roo.Toolbar.SplitButton = function(config){
27938     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27939 };
27940 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27941     render : function(td){
27942         this.td = td;
27943         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27944     },
27945     
27946     /**
27947      * Removes and destroys this button
27948      */
27949     destroy : function(){
27950         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27951         this.td.parentNode.removeChild(this.td);
27952     },
27953     
27954     /**
27955      * Shows this button
27956      */
27957     show: function(){
27958         this.hidden = false;
27959         this.td.style.display = "";
27960     },
27961     
27962     /**
27963      * Hides this button
27964      */
27965     hide: function(){
27966         this.hidden = true;
27967         this.td.style.display = "none";
27968     }
27969 });
27970
27971 // backwards compat
27972 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27973  * Based on:
27974  * Ext JS Library 1.1.1
27975  * Copyright(c) 2006-2007, Ext JS, LLC.
27976  *
27977  * Originally Released Under LGPL - original licence link has changed is not relivant.
27978  *
27979  * Fork - LGPL
27980  * <script type="text/javascript">
27981  */
27982  
27983 /**
27984  * @class Roo.PagingToolbar
27985  * @extends Roo.Toolbar
27986  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27987  * @constructor
27988  * Create a new PagingToolbar
27989  * @param {Object} config The config object
27990  */
27991 Roo.PagingToolbar = function(el, ds, config)
27992 {
27993     // old args format still supported... - xtype is prefered..
27994     if (typeof(el) == 'object' && el.xtype) {
27995         // created from xtype...
27996         config = el;
27997         ds = el.dataSource;
27998         el = config.container;
27999     }
28000     var items = [];
28001     if (config.items) {
28002         items = config.items;
28003         config.items = [];
28004     }
28005     
28006     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28007     this.ds = ds;
28008     this.cursor = 0;
28009     this.renderButtons(this.el);
28010     this.bind(ds);
28011     
28012     // supprot items array.
28013    
28014     Roo.each(items, function(e) {
28015         this.add(Roo.factory(e));
28016     },this);
28017     
28018 };
28019
28020 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28021     /**
28022      * @cfg {Roo.data.Store} dataSource
28023      * The underlying data store providing the paged data
28024      */
28025     /**
28026      * @cfg {String/HTMLElement/Element} container
28027      * container The id or element that will contain the toolbar
28028      */
28029     /**
28030      * @cfg {Boolean} displayInfo
28031      * True to display the displayMsg (defaults to false)
28032      */
28033     /**
28034      * @cfg {Number} pageSize
28035      * The number of records to display per page (defaults to 20)
28036      */
28037     pageSize: 20,
28038     /**
28039      * @cfg {String} displayMsg
28040      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28041      */
28042     displayMsg : 'Displaying {0} - {1} of {2}',
28043     /**
28044      * @cfg {String} emptyMsg
28045      * The message to display when no records are found (defaults to "No data to display")
28046      */
28047     emptyMsg : 'No data to display',
28048     /**
28049      * Customizable piece of the default paging text (defaults to "Page")
28050      * @type String
28051      */
28052     beforePageText : "Page",
28053     /**
28054      * Customizable piece of the default paging text (defaults to "of %0")
28055      * @type String
28056      */
28057     afterPageText : "of {0}",
28058     /**
28059      * Customizable piece of the default paging text (defaults to "First Page")
28060      * @type String
28061      */
28062     firstText : "First Page",
28063     /**
28064      * Customizable piece of the default paging text (defaults to "Previous Page")
28065      * @type String
28066      */
28067     prevText : "Previous Page",
28068     /**
28069      * Customizable piece of the default paging text (defaults to "Next Page")
28070      * @type String
28071      */
28072     nextText : "Next Page",
28073     /**
28074      * Customizable piece of the default paging text (defaults to "Last Page")
28075      * @type String
28076      */
28077     lastText : "Last Page",
28078     /**
28079      * Customizable piece of the default paging text (defaults to "Refresh")
28080      * @type String
28081      */
28082     refreshText : "Refresh",
28083
28084     // private
28085     renderButtons : function(el){
28086         Roo.PagingToolbar.superclass.render.call(this, el);
28087         this.first = this.addButton({
28088             tooltip: this.firstText,
28089             cls: "x-btn-icon x-grid-page-first",
28090             disabled: true,
28091             handler: this.onClick.createDelegate(this, ["first"])
28092         });
28093         this.prev = this.addButton({
28094             tooltip: this.prevText,
28095             cls: "x-btn-icon x-grid-page-prev",
28096             disabled: true,
28097             handler: this.onClick.createDelegate(this, ["prev"])
28098         });
28099         //this.addSeparator();
28100         this.add(this.beforePageText);
28101         this.field = Roo.get(this.addDom({
28102            tag: "input",
28103            type: "text",
28104            size: "3",
28105            value: "1",
28106            cls: "x-grid-page-number"
28107         }).el);
28108         this.field.on("keydown", this.onPagingKeydown, this);
28109         this.field.on("focus", function(){this.dom.select();});
28110         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28111         this.field.setHeight(18);
28112         //this.addSeparator();
28113         this.next = this.addButton({
28114             tooltip: this.nextText,
28115             cls: "x-btn-icon x-grid-page-next",
28116             disabled: true,
28117             handler: this.onClick.createDelegate(this, ["next"])
28118         });
28119         this.last = this.addButton({
28120             tooltip: this.lastText,
28121             cls: "x-btn-icon x-grid-page-last",
28122             disabled: true,
28123             handler: this.onClick.createDelegate(this, ["last"])
28124         });
28125         //this.addSeparator();
28126         this.loading = this.addButton({
28127             tooltip: this.refreshText,
28128             cls: "x-btn-icon x-grid-loading",
28129             handler: this.onClick.createDelegate(this, ["refresh"])
28130         });
28131
28132         if(this.displayInfo){
28133             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28134         }
28135     },
28136
28137     // private
28138     updateInfo : function(){
28139         if(this.displayEl){
28140             var count = this.ds.getCount();
28141             var msg = count == 0 ?
28142                 this.emptyMsg :
28143                 String.format(
28144                     this.displayMsg,
28145                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28146                 );
28147             this.displayEl.update(msg);
28148         }
28149     },
28150
28151     // private
28152     onLoad : function(ds, r, o){
28153        this.cursor = o.params ? o.params.start : 0;
28154        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28155
28156        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28157        this.field.dom.value = ap;
28158        this.first.setDisabled(ap == 1);
28159        this.prev.setDisabled(ap == 1);
28160        this.next.setDisabled(ap == ps);
28161        this.last.setDisabled(ap == ps);
28162        this.loading.enable();
28163        this.updateInfo();
28164     },
28165
28166     // private
28167     getPageData : function(){
28168         var total = this.ds.getTotalCount();
28169         return {
28170             total : total,
28171             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28172             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28173         };
28174     },
28175
28176     // private
28177     onLoadError : function(){
28178         this.loading.enable();
28179     },
28180
28181     // private
28182     onPagingKeydown : function(e){
28183         var k = e.getKey();
28184         var d = this.getPageData();
28185         if(k == e.RETURN){
28186             var v = this.field.dom.value, pageNum;
28187             if(!v || isNaN(pageNum = parseInt(v, 10))){
28188                 this.field.dom.value = d.activePage;
28189                 return;
28190             }
28191             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28192             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28193             e.stopEvent();
28194         }
28195         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))
28196         {
28197           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28198           this.field.dom.value = pageNum;
28199           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28200           e.stopEvent();
28201         }
28202         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28203         {
28204           var v = this.field.dom.value, pageNum; 
28205           var increment = (e.shiftKey) ? 10 : 1;
28206           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28207             increment *= -1;
28208           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28209             this.field.dom.value = d.activePage;
28210             return;
28211           }
28212           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28213           {
28214             this.field.dom.value = parseInt(v, 10) + increment;
28215             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28216             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28217           }
28218           e.stopEvent();
28219         }
28220     },
28221
28222     // private
28223     beforeLoad : function(){
28224         if(this.loading){
28225             this.loading.disable();
28226         }
28227     },
28228
28229     // private
28230     onClick : function(which){
28231         var ds = this.ds;
28232         switch(which){
28233             case "first":
28234                 ds.load({params:{start: 0, limit: this.pageSize}});
28235             break;
28236             case "prev":
28237                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28238             break;
28239             case "next":
28240                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28241             break;
28242             case "last":
28243                 var total = ds.getTotalCount();
28244                 var extra = total % this.pageSize;
28245                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28246                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28247             break;
28248             case "refresh":
28249                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28250             break;
28251         }
28252     },
28253
28254     /**
28255      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28256      * @param {Roo.data.Store} store The data store to unbind
28257      */
28258     unbind : function(ds){
28259         ds.un("beforeload", this.beforeLoad, this);
28260         ds.un("load", this.onLoad, this);
28261         ds.un("loadexception", this.onLoadError, this);
28262         ds.un("remove", this.updateInfo, this);
28263         ds.un("add", this.updateInfo, this);
28264         this.ds = undefined;
28265     },
28266
28267     /**
28268      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28269      * @param {Roo.data.Store} store The data store to bind
28270      */
28271     bind : function(ds){
28272         ds.on("beforeload", this.beforeLoad, this);
28273         ds.on("load", this.onLoad, this);
28274         ds.on("loadexception", this.onLoadError, this);
28275         ds.on("remove", this.updateInfo, this);
28276         ds.on("add", this.updateInfo, this);
28277         this.ds = ds;
28278     }
28279 });/*
28280  * Based on:
28281  * Ext JS Library 1.1.1
28282  * Copyright(c) 2006-2007, Ext JS, LLC.
28283  *
28284  * Originally Released Under LGPL - original licence link has changed is not relivant.
28285  *
28286  * Fork - LGPL
28287  * <script type="text/javascript">
28288  */
28289
28290 /**
28291  * @class Roo.Resizable
28292  * @extends Roo.util.Observable
28293  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28294  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28295  * 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
28296  * the element will be wrapped for you automatically.</p>
28297  * <p>Here is the list of valid resize handles:</p>
28298  * <pre>
28299 Value   Description
28300 ------  -------------------
28301  'n'     north
28302  's'     south
28303  'e'     east
28304  'w'     west
28305  'nw'    northwest
28306  'sw'    southwest
28307  'se'    southeast
28308  'ne'    northeast
28309  'hd'    horizontal drag
28310  'all'   all
28311 </pre>
28312  * <p>Here's an example showing the creation of a typical Resizable:</p>
28313  * <pre><code>
28314 var resizer = new Roo.Resizable("element-id", {
28315     handles: 'all',
28316     minWidth: 200,
28317     minHeight: 100,
28318     maxWidth: 500,
28319     maxHeight: 400,
28320     pinned: true
28321 });
28322 resizer.on("resize", myHandler);
28323 </code></pre>
28324  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28325  * resizer.east.setDisplayed(false);</p>
28326  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28327  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28328  * resize operation's new size (defaults to [0, 0])
28329  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28330  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28331  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28332  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28333  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28334  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28335  * @cfg {Number} width The width of the element in pixels (defaults to null)
28336  * @cfg {Number} height The height of the element in pixels (defaults to null)
28337  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28338  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28339  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28340  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28341  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28342  * in favor of the handles config option (defaults to false)
28343  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28344  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28345  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28346  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28347  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28348  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28349  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28350  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28351  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28352  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28353  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28354  * @constructor
28355  * Create a new resizable component
28356  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
28357  * @param {Object} config configuration options
28358   */
28359 Roo.Resizable = function(el, config)
28360 {
28361     this.el = Roo.get(el);
28362
28363     if(config && config.wrap){
28364         config.resizeChild = this.el;
28365         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
28366         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
28367         this.el.setStyle("overflow", "hidden");
28368         this.el.setPositioning(config.resizeChild.getPositioning());
28369         config.resizeChild.clearPositioning();
28370         if(!config.width || !config.height){
28371             var csize = config.resizeChild.getSize();
28372             this.el.setSize(csize.width, csize.height);
28373         }
28374         if(config.pinned && !config.adjustments){
28375             config.adjustments = "auto";
28376         }
28377     }
28378
28379     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
28380     this.proxy.unselectable();
28381     this.proxy.enableDisplayMode('block');
28382
28383     Roo.apply(this, config);
28384
28385     if(this.pinned){
28386         this.disableTrackOver = true;
28387         this.el.addClass("x-resizable-pinned");
28388     }
28389     // if the element isn't positioned, make it relative
28390     var position = this.el.getStyle("position");
28391     if(position != "absolute" && position != "fixed"){
28392         this.el.setStyle("position", "relative");
28393     }
28394     if(!this.handles){ // no handles passed, must be legacy style
28395         this.handles = 's,e,se';
28396         if(this.multiDirectional){
28397             this.handles += ',n,w';
28398         }
28399     }
28400     if(this.handles == "all"){
28401         this.handles = "n s e w ne nw se sw";
28402     }
28403     var hs = this.handles.split(/\s*?[,;]\s*?| /);
28404     var ps = Roo.Resizable.positions;
28405     for(var i = 0, len = hs.length; i < len; i++){
28406         if(hs[i] && ps[hs[i]]){
28407             var pos = ps[hs[i]];
28408             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
28409         }
28410     }
28411     // legacy
28412     this.corner = this.southeast;
28413     
28414     // updateBox = the box can move..
28415     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
28416         this.updateBox = true;
28417     }
28418
28419     this.activeHandle = null;
28420
28421     if(this.resizeChild){
28422         if(typeof this.resizeChild == "boolean"){
28423             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
28424         }else{
28425             this.resizeChild = Roo.get(this.resizeChild, true);
28426         }
28427     }
28428     
28429     if(this.adjustments == "auto"){
28430         var rc = this.resizeChild;
28431         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
28432         if(rc && (hw || hn)){
28433             rc.position("relative");
28434             rc.setLeft(hw ? hw.el.getWidth() : 0);
28435             rc.setTop(hn ? hn.el.getHeight() : 0);
28436         }
28437         this.adjustments = [
28438             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
28439             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
28440         ];
28441     }
28442
28443     if(this.draggable){
28444         this.dd = this.dynamic ?
28445             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
28446         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
28447     }
28448
28449     // public events
28450     this.addEvents({
28451         /**
28452          * @event beforeresize
28453          * Fired before resize is allowed. Set enabled to false to cancel resize.
28454          * @param {Roo.Resizable} this
28455          * @param {Roo.EventObject} e The mousedown event
28456          */
28457         "beforeresize" : true,
28458         /**
28459          * @event resize
28460          * Fired after a resize.
28461          * @param {Roo.Resizable} this
28462          * @param {Number} width The new width
28463          * @param {Number} height The new height
28464          * @param {Roo.EventObject} e The mouseup event
28465          */
28466         "resize" : true
28467     });
28468
28469     if(this.width !== null && this.height !== null){
28470         this.resizeTo(this.width, this.height);
28471     }else{
28472         this.updateChildSize();
28473     }
28474     if(Roo.isIE){
28475         this.el.dom.style.zoom = 1;
28476     }
28477     Roo.Resizable.superclass.constructor.call(this);
28478 };
28479
28480 Roo.extend(Roo.Resizable, Roo.util.Observable, {
28481         resizeChild : false,
28482         adjustments : [0, 0],
28483         minWidth : 5,
28484         minHeight : 5,
28485         maxWidth : 10000,
28486         maxHeight : 10000,
28487         enabled : true,
28488         animate : false,
28489         duration : .35,
28490         dynamic : false,
28491         handles : false,
28492         multiDirectional : false,
28493         disableTrackOver : false,
28494         easing : 'easeOutStrong',
28495         widthIncrement : 0,
28496         heightIncrement : 0,
28497         pinned : false,
28498         width : null,
28499         height : null,
28500         preserveRatio : false,
28501         transparent: false,
28502         minX: 0,
28503         minY: 0,
28504         draggable: false,
28505
28506         /**
28507          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
28508          */
28509         constrainTo: undefined,
28510         /**
28511          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
28512          */
28513         resizeRegion: undefined,
28514
28515
28516     /**
28517      * Perform a manual resize
28518      * @param {Number} width
28519      * @param {Number} height
28520      */
28521     resizeTo : function(width, height){
28522         this.el.setSize(width, height);
28523         this.updateChildSize();
28524         this.fireEvent("resize", this, width, height, null);
28525     },
28526
28527     // private
28528     startSizing : function(e, handle){
28529         this.fireEvent("beforeresize", this, e);
28530         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
28531
28532             if(!this.overlay){
28533                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
28534                 this.overlay.unselectable();
28535                 this.overlay.enableDisplayMode("block");
28536                 this.overlay.on("mousemove", this.onMouseMove, this);
28537                 this.overlay.on("mouseup", this.onMouseUp, this);
28538             }
28539             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
28540
28541             this.resizing = true;
28542             this.startBox = this.el.getBox();
28543             this.startPoint = e.getXY();
28544             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
28545                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
28546
28547             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28548             this.overlay.show();
28549
28550             if(this.constrainTo) {
28551                 var ct = Roo.get(this.constrainTo);
28552                 this.resizeRegion = ct.getRegion().adjust(
28553                     ct.getFrameWidth('t'),
28554                     ct.getFrameWidth('l'),
28555                     -ct.getFrameWidth('b'),
28556                     -ct.getFrameWidth('r')
28557                 );
28558             }
28559
28560             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
28561             this.proxy.show();
28562             this.proxy.setBox(this.startBox);
28563             if(!this.dynamic){
28564                 this.proxy.setStyle('visibility', 'visible');
28565             }
28566         }
28567     },
28568
28569     // private
28570     onMouseDown : function(handle, e){
28571         if(this.enabled){
28572             e.stopEvent();
28573             this.activeHandle = handle;
28574             this.startSizing(e, handle);
28575         }
28576     },
28577
28578     // private
28579     onMouseUp : function(e){
28580         var size = this.resizeElement();
28581         this.resizing = false;
28582         this.handleOut();
28583         this.overlay.hide();
28584         this.proxy.hide();
28585         this.fireEvent("resize", this, size.width, size.height, e);
28586     },
28587
28588     // private
28589     updateChildSize : function(){
28590         if(this.resizeChild){
28591             var el = this.el;
28592             var child = this.resizeChild;
28593             var adj = this.adjustments;
28594             if(el.dom.offsetWidth){
28595                 var b = el.getSize(true);
28596                 child.setSize(b.width+adj[0], b.height+adj[1]);
28597             }
28598             // Second call here for IE
28599             // The first call enables instant resizing and
28600             // the second call corrects scroll bars if they
28601             // exist
28602             if(Roo.isIE){
28603                 setTimeout(function(){
28604                     if(el.dom.offsetWidth){
28605                         var b = el.getSize(true);
28606                         child.setSize(b.width+adj[0], b.height+adj[1]);
28607                     }
28608                 }, 10);
28609             }
28610         }
28611     },
28612
28613     // private
28614     snap : function(value, inc, min){
28615         if(!inc || !value) return value;
28616         var newValue = value;
28617         var m = value % inc;
28618         if(m > 0){
28619             if(m > (inc/2)){
28620                 newValue = value + (inc-m);
28621             }else{
28622                 newValue = value - m;
28623             }
28624         }
28625         return Math.max(min, newValue);
28626     },
28627
28628     // private
28629     resizeElement : function(){
28630         var box = this.proxy.getBox();
28631         if(this.updateBox){
28632             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28633         }else{
28634             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28635         }
28636         this.updateChildSize();
28637         if(!this.dynamic){
28638             this.proxy.hide();
28639         }
28640         return box;
28641     },
28642
28643     // private
28644     constrain : function(v, diff, m, mx){
28645         if(v - diff < m){
28646             diff = v - m;
28647         }else if(v - diff > mx){
28648             diff = mx - v;
28649         }
28650         return diff;
28651     },
28652
28653     // private
28654     onMouseMove : function(e){
28655         if(this.enabled){
28656             try{// try catch so if something goes wrong the user doesn't get hung
28657
28658             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28659                 return;
28660             }
28661
28662             //var curXY = this.startPoint;
28663             var curSize = this.curSize || this.startBox;
28664             var x = this.startBox.x, y = this.startBox.y;
28665             var ox = x, oy = y;
28666             var w = curSize.width, h = curSize.height;
28667             var ow = w, oh = h;
28668             var mw = this.minWidth, mh = this.minHeight;
28669             var mxw = this.maxWidth, mxh = this.maxHeight;
28670             var wi = this.widthIncrement;
28671             var hi = this.heightIncrement;
28672
28673             var eventXY = e.getXY();
28674             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28675             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28676
28677             var pos = this.activeHandle.position;
28678
28679             switch(pos){
28680                 case "east":
28681                     w += diffX;
28682                     w = Math.min(Math.max(mw, w), mxw);
28683                     break;
28684              
28685                 case "south":
28686                     h += diffY;
28687                     h = Math.min(Math.max(mh, h), mxh);
28688                     break;
28689                 case "southeast":
28690                     w += diffX;
28691                     h += diffY;
28692                     w = Math.min(Math.max(mw, w), mxw);
28693                     h = Math.min(Math.max(mh, h), mxh);
28694                     break;
28695                 case "north":
28696                     diffY = this.constrain(h, diffY, mh, mxh);
28697                     y += diffY;
28698                     h -= diffY;
28699                     break;
28700                 case "hdrag":
28701                     
28702                     if (wi) {
28703                         var adiffX = Math.abs(diffX);
28704                         var sub = (adiffX % wi); // how much 
28705                         if (sub > (wi/2)) { // far enough to snap
28706                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28707                         } else {
28708                             // remove difference.. 
28709                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28710                         }
28711                     }
28712                     x += diffX;
28713                     x = Math.max(this.minX, x);
28714                     break;
28715                 case "west":
28716                     diffX = this.constrain(w, diffX, mw, mxw);
28717                     x += diffX;
28718                     w -= diffX;
28719                     break;
28720                 case "northeast":
28721                     w += diffX;
28722                     w = Math.min(Math.max(mw, w), mxw);
28723                     diffY = this.constrain(h, diffY, mh, mxh);
28724                     y += diffY;
28725                     h -= diffY;
28726                     break;
28727                 case "northwest":
28728                     diffX = this.constrain(w, diffX, mw, mxw);
28729                     diffY = this.constrain(h, diffY, mh, mxh);
28730                     y += diffY;
28731                     h -= diffY;
28732                     x += diffX;
28733                     w -= diffX;
28734                     break;
28735                case "southwest":
28736                     diffX = this.constrain(w, diffX, mw, mxw);
28737                     h += diffY;
28738                     h = Math.min(Math.max(mh, h), mxh);
28739                     x += diffX;
28740                     w -= diffX;
28741                     break;
28742             }
28743
28744             var sw = this.snap(w, wi, mw);
28745             var sh = this.snap(h, hi, mh);
28746             if(sw != w || sh != h){
28747                 switch(pos){
28748                     case "northeast":
28749                         y -= sh - h;
28750                     break;
28751                     case "north":
28752                         y -= sh - h;
28753                         break;
28754                     case "southwest":
28755                         x -= sw - w;
28756                     break;
28757                     case "west":
28758                         x -= sw - w;
28759                         break;
28760                     case "northwest":
28761                         x -= sw - w;
28762                         y -= sh - h;
28763                     break;
28764                 }
28765                 w = sw;
28766                 h = sh;
28767             }
28768
28769             if(this.preserveRatio){
28770                 switch(pos){
28771                     case "southeast":
28772                     case "east":
28773                         h = oh * (w/ow);
28774                         h = Math.min(Math.max(mh, h), mxh);
28775                         w = ow * (h/oh);
28776                        break;
28777                     case "south":
28778                         w = ow * (h/oh);
28779                         w = Math.min(Math.max(mw, w), mxw);
28780                         h = oh * (w/ow);
28781                         break;
28782                     case "northeast":
28783                         w = ow * (h/oh);
28784                         w = Math.min(Math.max(mw, w), mxw);
28785                         h = oh * (w/ow);
28786                     break;
28787                     case "north":
28788                         var tw = w;
28789                         w = ow * (h/oh);
28790                         w = Math.min(Math.max(mw, w), mxw);
28791                         h = oh * (w/ow);
28792                         x += (tw - w) / 2;
28793                         break;
28794                     case "southwest":
28795                         h = oh * (w/ow);
28796                         h = Math.min(Math.max(mh, h), mxh);
28797                         var tw = w;
28798                         w = ow * (h/oh);
28799                         x += tw - w;
28800                         break;
28801                     case "west":
28802                         var th = h;
28803                         h = oh * (w/ow);
28804                         h = Math.min(Math.max(mh, h), mxh);
28805                         y += (th - h) / 2;
28806                         var tw = w;
28807                         w = ow * (h/oh);
28808                         x += tw - w;
28809                        break;
28810                     case "northwest":
28811                         var tw = w;
28812                         var th = h;
28813                         h = oh * (w/ow);
28814                         h = Math.min(Math.max(mh, h), mxh);
28815                         w = ow * (h/oh);
28816                         y += th - h;
28817                         x += tw - w;
28818                        break;
28819
28820                 }
28821             }
28822             if (pos == 'hdrag') {
28823                 w = ow;
28824             }
28825             this.proxy.setBounds(x, y, w, h);
28826             if(this.dynamic){
28827                 this.resizeElement();
28828             }
28829             }catch(e){}
28830         }
28831     },
28832
28833     // private
28834     handleOver : function(){
28835         if(this.enabled){
28836             this.el.addClass("x-resizable-over");
28837         }
28838     },
28839
28840     // private
28841     handleOut : function(){
28842         if(!this.resizing){
28843             this.el.removeClass("x-resizable-over");
28844         }
28845     },
28846
28847     /**
28848      * Returns the element this component is bound to.
28849      * @return {Roo.Element}
28850      */
28851     getEl : function(){
28852         return this.el;
28853     },
28854
28855     /**
28856      * Returns the resizeChild element (or null).
28857      * @return {Roo.Element}
28858      */
28859     getResizeChild : function(){
28860         return this.resizeChild;
28861     },
28862
28863     /**
28864      * Destroys this resizable. If the element was wrapped and
28865      * removeEl is not true then the element remains.
28866      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28867      */
28868     destroy : function(removeEl){
28869         this.proxy.remove();
28870         if(this.overlay){
28871             this.overlay.removeAllListeners();
28872             this.overlay.remove();
28873         }
28874         var ps = Roo.Resizable.positions;
28875         for(var k in ps){
28876             if(typeof ps[k] != "function" && this[ps[k]]){
28877                 var h = this[ps[k]];
28878                 h.el.removeAllListeners();
28879                 h.el.remove();
28880             }
28881         }
28882         if(removeEl){
28883             this.el.update("");
28884             this.el.remove();
28885         }
28886     }
28887 });
28888
28889 // private
28890 // hash to map config positions to true positions
28891 Roo.Resizable.positions = {
28892     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28893     hd: "hdrag"
28894 };
28895
28896 // private
28897 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28898     if(!this.tpl){
28899         // only initialize the template if resizable is used
28900         var tpl = Roo.DomHelper.createTemplate(
28901             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28902         );
28903         tpl.compile();
28904         Roo.Resizable.Handle.prototype.tpl = tpl;
28905     }
28906     this.position = pos;
28907     this.rz = rz;
28908     // show north drag fro topdra
28909     var handlepos = pos == 'hdrag' ? 'north' : pos;
28910     
28911     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28912     if (pos == 'hdrag') {
28913         this.el.setStyle('cursor', 'pointer');
28914     }
28915     this.el.unselectable();
28916     if(transparent){
28917         this.el.setOpacity(0);
28918     }
28919     this.el.on("mousedown", this.onMouseDown, this);
28920     if(!disableTrackOver){
28921         this.el.on("mouseover", this.onMouseOver, this);
28922         this.el.on("mouseout", this.onMouseOut, this);
28923     }
28924 };
28925
28926 // private
28927 Roo.Resizable.Handle.prototype = {
28928     afterResize : function(rz){
28929         // do nothing
28930     },
28931     // private
28932     onMouseDown : function(e){
28933         this.rz.onMouseDown(this, e);
28934     },
28935     // private
28936     onMouseOver : function(e){
28937         this.rz.handleOver(this, e);
28938     },
28939     // private
28940     onMouseOut : function(e){
28941         this.rz.handleOut(this, e);
28942     }
28943 };/*
28944  * Based on:
28945  * Ext JS Library 1.1.1
28946  * Copyright(c) 2006-2007, Ext JS, LLC.
28947  *
28948  * Originally Released Under LGPL - original licence link has changed is not relivant.
28949  *
28950  * Fork - LGPL
28951  * <script type="text/javascript">
28952  */
28953
28954 /**
28955  * @class Roo.Editor
28956  * @extends Roo.Component
28957  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28958  * @constructor
28959  * Create a new Editor
28960  * @param {Roo.form.Field} field The Field object (or descendant)
28961  * @param {Object} config The config object
28962  */
28963 Roo.Editor = function(field, config){
28964     Roo.Editor.superclass.constructor.call(this, config);
28965     this.field = field;
28966     this.addEvents({
28967         /**
28968              * @event beforestartedit
28969              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28970              * false from the handler of this event.
28971              * @param {Editor} this
28972              * @param {Roo.Element} boundEl The underlying element bound to this editor
28973              * @param {Mixed} value The field value being set
28974              */
28975         "beforestartedit" : true,
28976         /**
28977              * @event startedit
28978              * Fires when this editor is displayed
28979              * @param {Roo.Element} boundEl The underlying element bound to this editor
28980              * @param {Mixed} value The starting field value
28981              */
28982         "startedit" : true,
28983         /**
28984              * @event beforecomplete
28985              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28986              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28987              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28988              * event will not fire since no edit actually occurred.
28989              * @param {Editor} this
28990              * @param {Mixed} value The current field value
28991              * @param {Mixed} startValue The original field value
28992              */
28993         "beforecomplete" : true,
28994         /**
28995              * @event complete
28996              * Fires after editing is complete and any changed value has been written to the underlying field.
28997              * @param {Editor} this
28998              * @param {Mixed} value The current field value
28999              * @param {Mixed} startValue The original field value
29000              */
29001         "complete" : true,
29002         /**
29003          * @event specialkey
29004          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29005          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29006          * @param {Roo.form.Field} this
29007          * @param {Roo.EventObject} e The event object
29008          */
29009         "specialkey" : true
29010     });
29011 };
29012
29013 Roo.extend(Roo.Editor, Roo.Component, {
29014     /**
29015      * @cfg {Boolean/String} autosize
29016      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29017      * or "height" to adopt the height only (defaults to false)
29018      */
29019     /**
29020      * @cfg {Boolean} revertInvalid
29021      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29022      * validation fails (defaults to true)
29023      */
29024     /**
29025      * @cfg {Boolean} ignoreNoChange
29026      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29027      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29028      * will never be ignored.
29029      */
29030     /**
29031      * @cfg {Boolean} hideEl
29032      * False to keep the bound element visible while the editor is displayed (defaults to true)
29033      */
29034     /**
29035      * @cfg {Mixed} value
29036      * The data value of the underlying field (defaults to "")
29037      */
29038     value : "",
29039     /**
29040      * @cfg {String} alignment
29041      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29042      */
29043     alignment: "c-c?",
29044     /**
29045      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29046      * for bottom-right shadow (defaults to "frame")
29047      */
29048     shadow : "frame",
29049     /**
29050      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29051      */
29052     constrain : false,
29053     /**
29054      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29055      */
29056     completeOnEnter : false,
29057     /**
29058      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29059      */
29060     cancelOnEsc : false,
29061     /**
29062      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29063      */
29064     updateEl : false,
29065
29066     // private
29067     onRender : function(ct, position){
29068         this.el = new Roo.Layer({
29069             shadow: this.shadow,
29070             cls: "x-editor",
29071             parentEl : ct,
29072             shim : this.shim,
29073             shadowOffset:4,
29074             id: this.id,
29075             constrain: this.constrain
29076         });
29077         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29078         if(this.field.msgTarget != 'title'){
29079             this.field.msgTarget = 'qtip';
29080         }
29081         this.field.render(this.el);
29082         if(Roo.isGecko){
29083             this.field.el.dom.setAttribute('autocomplete', 'off');
29084         }
29085         this.field.on("specialkey", this.onSpecialKey, this);
29086         if(this.swallowKeys){
29087             this.field.el.swallowEvent(['keydown','keypress']);
29088         }
29089         this.field.show();
29090         this.field.on("blur", this.onBlur, this);
29091         if(this.field.grow){
29092             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29093         }
29094     },
29095
29096     onSpecialKey : function(field, e)
29097     {
29098         //Roo.log('editor onSpecialKey');
29099         if(this.completeOnEnter && e.getKey() == e.ENTER){
29100             e.stopEvent();
29101             this.completeEdit();
29102             return;
29103         }
29104         // do not fire special key otherwise it might hide close the editor...
29105         if(e.getKey() == e.ENTER){    
29106             return;
29107         }
29108         if(this.cancelOnEsc && e.getKey() == e.ESC){
29109             this.cancelEdit();
29110             return;
29111         } 
29112         this.fireEvent('specialkey', field, e);
29113     
29114     },
29115
29116     /**
29117      * Starts the editing process and shows the editor.
29118      * @param {String/HTMLElement/Element} el The element to edit
29119      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29120       * to the innerHTML of el.
29121      */
29122     startEdit : function(el, value){
29123         if(this.editing){
29124             this.completeEdit();
29125         }
29126         this.boundEl = Roo.get(el);
29127         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29128         if(!this.rendered){
29129             this.render(this.parentEl || document.body);
29130         }
29131         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29132             return;
29133         }
29134         this.startValue = v;
29135         this.field.setValue(v);
29136         if(this.autoSize){
29137             var sz = this.boundEl.getSize();
29138             switch(this.autoSize){
29139                 case "width":
29140                 this.setSize(sz.width,  "");
29141                 break;
29142                 case "height":
29143                 this.setSize("",  sz.height);
29144                 break;
29145                 default:
29146                 this.setSize(sz.width,  sz.height);
29147             }
29148         }
29149         this.el.alignTo(this.boundEl, this.alignment);
29150         this.editing = true;
29151         if(Roo.QuickTips){
29152             Roo.QuickTips.disable();
29153         }
29154         this.show();
29155     },
29156
29157     /**
29158      * Sets the height and width of this editor.
29159      * @param {Number} width The new width
29160      * @param {Number} height The new height
29161      */
29162     setSize : function(w, h){
29163         this.field.setSize(w, h);
29164         if(this.el){
29165             this.el.sync();
29166         }
29167     },
29168
29169     /**
29170      * Realigns the editor to the bound field based on the current alignment config value.
29171      */
29172     realign : function(){
29173         this.el.alignTo(this.boundEl, this.alignment);
29174     },
29175
29176     /**
29177      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29178      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29179      */
29180     completeEdit : function(remainVisible){
29181         if(!this.editing){
29182             return;
29183         }
29184         var v = this.getValue();
29185         if(this.revertInvalid !== false && !this.field.isValid()){
29186             v = this.startValue;
29187             this.cancelEdit(true);
29188         }
29189         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29190             this.editing = false;
29191             this.hide();
29192             return;
29193         }
29194         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29195             this.editing = false;
29196             if(this.updateEl && this.boundEl){
29197                 this.boundEl.update(v);
29198             }
29199             if(remainVisible !== true){
29200                 this.hide();
29201             }
29202             this.fireEvent("complete", this, v, this.startValue);
29203         }
29204     },
29205
29206     // private
29207     onShow : function(){
29208         this.el.show();
29209         if(this.hideEl !== false){
29210             this.boundEl.hide();
29211         }
29212         this.field.show();
29213         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29214             this.fixIEFocus = true;
29215             this.deferredFocus.defer(50, this);
29216         }else{
29217             this.field.focus();
29218         }
29219         this.fireEvent("startedit", this.boundEl, this.startValue);
29220     },
29221
29222     deferredFocus : function(){
29223         if(this.editing){
29224             this.field.focus();
29225         }
29226     },
29227
29228     /**
29229      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29230      * reverted to the original starting value.
29231      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29232      * cancel (defaults to false)
29233      */
29234     cancelEdit : function(remainVisible){
29235         if(this.editing){
29236             this.setValue(this.startValue);
29237             if(remainVisible !== true){
29238                 this.hide();
29239             }
29240         }
29241     },
29242
29243     // private
29244     onBlur : function(){
29245         if(this.allowBlur !== true && this.editing){
29246             this.completeEdit();
29247         }
29248     },
29249
29250     // private
29251     onHide : function(){
29252         if(this.editing){
29253             this.completeEdit();
29254             return;
29255         }
29256         this.field.blur();
29257         if(this.field.collapse){
29258             this.field.collapse();
29259         }
29260         this.el.hide();
29261         if(this.hideEl !== false){
29262             this.boundEl.show();
29263         }
29264         if(Roo.QuickTips){
29265             Roo.QuickTips.enable();
29266         }
29267     },
29268
29269     /**
29270      * Sets the data value of the editor
29271      * @param {Mixed} value Any valid value supported by the underlying field
29272      */
29273     setValue : function(v){
29274         this.field.setValue(v);
29275     },
29276
29277     /**
29278      * Gets the data value of the editor
29279      * @return {Mixed} The data value
29280      */
29281     getValue : function(){
29282         return this.field.getValue();
29283     }
29284 });/*
29285  * Based on:
29286  * Ext JS Library 1.1.1
29287  * Copyright(c) 2006-2007, Ext JS, LLC.
29288  *
29289  * Originally Released Under LGPL - original licence link has changed is not relivant.
29290  *
29291  * Fork - LGPL
29292  * <script type="text/javascript">
29293  */
29294  
29295 /**
29296  * @class Roo.BasicDialog
29297  * @extends Roo.util.Observable
29298  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29299  * <pre><code>
29300 var dlg = new Roo.BasicDialog("my-dlg", {
29301     height: 200,
29302     width: 300,
29303     minHeight: 100,
29304     minWidth: 150,
29305     modal: true,
29306     proxyDrag: true,
29307     shadow: true
29308 });
29309 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29310 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29311 dlg.addButton('Cancel', dlg.hide, dlg);
29312 dlg.show();
29313 </code></pre>
29314   <b>A Dialog should always be a direct child of the body element.</b>
29315  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29316  * @cfg {String} title Default text to display in the title bar (defaults to null)
29317  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29318  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29319  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29320  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29321  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29322  * (defaults to null with no animation)
29323  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29324  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29325  * property for valid values (defaults to 'all')
29326  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29327  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29328  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29329  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29330  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29331  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29332  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29333  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29334  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29335  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29336  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29337  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
29338  * draggable = true (defaults to false)
29339  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
29340  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29341  * shadow (defaults to false)
29342  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
29343  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
29344  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
29345  * @cfg {Array} buttons Array of buttons
29346  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
29347  * @constructor
29348  * Create a new BasicDialog.
29349  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
29350  * @param {Object} config Configuration options
29351  */
29352 Roo.BasicDialog = function(el, config){
29353     this.el = Roo.get(el);
29354     var dh = Roo.DomHelper;
29355     if(!this.el && config && config.autoCreate){
29356         if(typeof config.autoCreate == "object"){
29357             if(!config.autoCreate.id){
29358                 config.autoCreate.id = el;
29359             }
29360             this.el = dh.append(document.body,
29361                         config.autoCreate, true);
29362         }else{
29363             this.el = dh.append(document.body,
29364                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
29365         }
29366     }
29367     el = this.el;
29368     el.setDisplayed(true);
29369     el.hide = this.hideAction;
29370     this.id = el.id;
29371     el.addClass("x-dlg");
29372
29373     Roo.apply(this, config);
29374
29375     this.proxy = el.createProxy("x-dlg-proxy");
29376     this.proxy.hide = this.hideAction;
29377     this.proxy.setOpacity(.5);
29378     this.proxy.hide();
29379
29380     if(config.width){
29381         el.setWidth(config.width);
29382     }
29383     if(config.height){
29384         el.setHeight(config.height);
29385     }
29386     this.size = el.getSize();
29387     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
29388         this.xy = [config.x,config.y];
29389     }else{
29390         this.xy = el.getCenterXY(true);
29391     }
29392     /** The header element @type Roo.Element */
29393     this.header = el.child("> .x-dlg-hd");
29394     /** The body element @type Roo.Element */
29395     this.body = el.child("> .x-dlg-bd");
29396     /** The footer element @type Roo.Element */
29397     this.footer = el.child("> .x-dlg-ft");
29398
29399     if(!this.header){
29400         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
29401     }
29402     if(!this.body){
29403         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
29404     }
29405
29406     this.header.unselectable();
29407     if(this.title){
29408         this.header.update(this.title);
29409     }
29410     // this element allows the dialog to be focused for keyboard event
29411     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
29412     this.focusEl.swallowEvent("click", true);
29413
29414     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
29415
29416     // wrap the body and footer for special rendering
29417     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
29418     if(this.footer){
29419         this.bwrap.dom.appendChild(this.footer.dom);
29420     }
29421
29422     this.bg = this.el.createChild({
29423         tag: "div", cls:"x-dlg-bg",
29424         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
29425     });
29426     this.centerBg = this.bg.child("div.x-dlg-bg-center");
29427
29428
29429     if(this.autoScroll !== false && !this.autoTabs){
29430         this.body.setStyle("overflow", "auto");
29431     }
29432
29433     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
29434
29435     if(this.closable !== false){
29436         this.el.addClass("x-dlg-closable");
29437         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
29438         this.close.on("click", this.closeClick, this);
29439         this.close.addClassOnOver("x-dlg-close-over");
29440     }
29441     if(this.collapsible !== false){
29442         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
29443         this.collapseBtn.on("click", this.collapseClick, this);
29444         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
29445         this.header.on("dblclick", this.collapseClick, this);
29446     }
29447     if(this.resizable !== false){
29448         this.el.addClass("x-dlg-resizable");
29449         this.resizer = new Roo.Resizable(el, {
29450             minWidth: this.minWidth || 80,
29451             minHeight:this.minHeight || 80,
29452             handles: this.resizeHandles || "all",
29453             pinned: true
29454         });
29455         this.resizer.on("beforeresize", this.beforeResize, this);
29456         this.resizer.on("resize", this.onResize, this);
29457     }
29458     if(this.draggable !== false){
29459         el.addClass("x-dlg-draggable");
29460         if (!this.proxyDrag) {
29461             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
29462         }
29463         else {
29464             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
29465         }
29466         dd.setHandleElId(this.header.id);
29467         dd.endDrag = this.endMove.createDelegate(this);
29468         dd.startDrag = this.startMove.createDelegate(this);
29469         dd.onDrag = this.onDrag.createDelegate(this);
29470         dd.scroll = false;
29471         this.dd = dd;
29472     }
29473     if(this.modal){
29474         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
29475         this.mask.enableDisplayMode("block");
29476         this.mask.hide();
29477         this.el.addClass("x-dlg-modal");
29478     }
29479     if(this.shadow){
29480         this.shadow = new Roo.Shadow({
29481             mode : typeof this.shadow == "string" ? this.shadow : "sides",
29482             offset : this.shadowOffset
29483         });
29484     }else{
29485         this.shadowOffset = 0;
29486     }
29487     if(Roo.useShims && this.shim !== false){
29488         this.shim = this.el.createShim();
29489         this.shim.hide = this.hideAction;
29490         this.shim.hide();
29491     }else{
29492         this.shim = false;
29493     }
29494     if(this.autoTabs){
29495         this.initTabs();
29496     }
29497     if (this.buttons) { 
29498         var bts= this.buttons;
29499         this.buttons = [];
29500         Roo.each(bts, function(b) {
29501             this.addButton(b);
29502         }, this);
29503     }
29504     
29505     
29506     this.addEvents({
29507         /**
29508          * @event keydown
29509          * Fires when a key is pressed
29510          * @param {Roo.BasicDialog} this
29511          * @param {Roo.EventObject} e
29512          */
29513         "keydown" : true,
29514         /**
29515          * @event move
29516          * Fires when this dialog is moved by the user.
29517          * @param {Roo.BasicDialog} this
29518          * @param {Number} x The new page X
29519          * @param {Number} y The new page Y
29520          */
29521         "move" : true,
29522         /**
29523          * @event resize
29524          * Fires when this dialog is resized by the user.
29525          * @param {Roo.BasicDialog} this
29526          * @param {Number} width The new width
29527          * @param {Number} height The new height
29528          */
29529         "resize" : true,
29530         /**
29531          * @event beforehide
29532          * Fires before this dialog is hidden.
29533          * @param {Roo.BasicDialog} this
29534          */
29535         "beforehide" : true,
29536         /**
29537          * @event hide
29538          * Fires when this dialog is hidden.
29539          * @param {Roo.BasicDialog} this
29540          */
29541         "hide" : true,
29542         /**
29543          * @event beforeshow
29544          * Fires before this dialog is shown.
29545          * @param {Roo.BasicDialog} this
29546          */
29547         "beforeshow" : true,
29548         /**
29549          * @event show
29550          * Fires when this dialog is shown.
29551          * @param {Roo.BasicDialog} this
29552          */
29553         "show" : true
29554     });
29555     el.on("keydown", this.onKeyDown, this);
29556     el.on("mousedown", this.toFront, this);
29557     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
29558     this.el.hide();
29559     Roo.DialogManager.register(this);
29560     Roo.BasicDialog.superclass.constructor.call(this);
29561 };
29562
29563 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29564     shadowOffset: Roo.isIE ? 6 : 5,
29565     minHeight: 80,
29566     minWidth: 200,
29567     minButtonWidth: 75,
29568     defaultButton: null,
29569     buttonAlign: "right",
29570     tabTag: 'div',
29571     firstShow: true,
29572
29573     /**
29574      * Sets the dialog title text
29575      * @param {String} text The title text to display
29576      * @return {Roo.BasicDialog} this
29577      */
29578     setTitle : function(text){
29579         this.header.update(text);
29580         return this;
29581     },
29582
29583     // private
29584     closeClick : function(){
29585         this.hide();
29586     },
29587
29588     // private
29589     collapseClick : function(){
29590         this[this.collapsed ? "expand" : "collapse"]();
29591     },
29592
29593     /**
29594      * Collapses the dialog to its minimized state (only the title bar is visible).
29595      * Equivalent to the user clicking the collapse dialog button.
29596      */
29597     collapse : function(){
29598         if(!this.collapsed){
29599             this.collapsed = true;
29600             this.el.addClass("x-dlg-collapsed");
29601             this.restoreHeight = this.el.getHeight();
29602             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29603         }
29604     },
29605
29606     /**
29607      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29608      * clicking the expand dialog button.
29609      */
29610     expand : function(){
29611         if(this.collapsed){
29612             this.collapsed = false;
29613             this.el.removeClass("x-dlg-collapsed");
29614             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29615         }
29616     },
29617
29618     /**
29619      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29620      * @return {Roo.TabPanel} The tabs component
29621      */
29622     initTabs : function(){
29623         var tabs = this.getTabs();
29624         while(tabs.getTab(0)){
29625             tabs.removeTab(0);
29626         }
29627         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29628             var dom = el.dom;
29629             tabs.addTab(Roo.id(dom), dom.title);
29630             dom.title = "";
29631         });
29632         tabs.activate(0);
29633         return tabs;
29634     },
29635
29636     // private
29637     beforeResize : function(){
29638         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29639     },
29640
29641     // private
29642     onResize : function(){
29643         this.refreshSize();
29644         this.syncBodyHeight();
29645         this.adjustAssets();
29646         this.focus();
29647         this.fireEvent("resize", this, this.size.width, this.size.height);
29648     },
29649
29650     // private
29651     onKeyDown : function(e){
29652         if(this.isVisible()){
29653             this.fireEvent("keydown", this, e);
29654         }
29655     },
29656
29657     /**
29658      * Resizes the dialog.
29659      * @param {Number} width
29660      * @param {Number} height
29661      * @return {Roo.BasicDialog} this
29662      */
29663     resizeTo : function(width, height){
29664         this.el.setSize(width, height);
29665         this.size = {width: width, height: height};
29666         this.syncBodyHeight();
29667         if(this.fixedcenter){
29668             this.center();
29669         }
29670         if(this.isVisible()){
29671             this.constrainXY();
29672             this.adjustAssets();
29673         }
29674         this.fireEvent("resize", this, width, height);
29675         return this;
29676     },
29677
29678
29679     /**
29680      * Resizes the dialog to fit the specified content size.
29681      * @param {Number} width
29682      * @param {Number} height
29683      * @return {Roo.BasicDialog} this
29684      */
29685     setContentSize : function(w, h){
29686         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29687         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29688         //if(!this.el.isBorderBox()){
29689             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29690             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29691         //}
29692         if(this.tabs){
29693             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29694             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29695         }
29696         this.resizeTo(w, h);
29697         return this;
29698     },
29699
29700     /**
29701      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29702      * executed in response to a particular key being pressed while the dialog is active.
29703      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29704      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29705      * @param {Function} fn The function to call
29706      * @param {Object} scope (optional) The scope of the function
29707      * @return {Roo.BasicDialog} this
29708      */
29709     addKeyListener : function(key, fn, scope){
29710         var keyCode, shift, ctrl, alt;
29711         if(typeof key == "object" && !(key instanceof Array)){
29712             keyCode = key["key"];
29713             shift = key["shift"];
29714             ctrl = key["ctrl"];
29715             alt = key["alt"];
29716         }else{
29717             keyCode = key;
29718         }
29719         var handler = function(dlg, e){
29720             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29721                 var k = e.getKey();
29722                 if(keyCode instanceof Array){
29723                     for(var i = 0, len = keyCode.length; i < len; i++){
29724                         if(keyCode[i] == k){
29725                           fn.call(scope || window, dlg, k, e);
29726                           return;
29727                         }
29728                     }
29729                 }else{
29730                     if(k == keyCode){
29731                         fn.call(scope || window, dlg, k, e);
29732                     }
29733                 }
29734             }
29735         };
29736         this.on("keydown", handler);
29737         return this;
29738     },
29739
29740     /**
29741      * Returns the TabPanel component (creates it if it doesn't exist).
29742      * Note: If you wish to simply check for the existence of tabs without creating them,
29743      * check for a null 'tabs' property.
29744      * @return {Roo.TabPanel} The tabs component
29745      */
29746     getTabs : function(){
29747         if(!this.tabs){
29748             this.el.addClass("x-dlg-auto-tabs");
29749             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29750             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29751         }
29752         return this.tabs;
29753     },
29754
29755     /**
29756      * Adds a button to the footer section of the dialog.
29757      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29758      * object or a valid Roo.DomHelper element config
29759      * @param {Function} handler The function called when the button is clicked
29760      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29761      * @return {Roo.Button} The new button
29762      */
29763     addButton : function(config, handler, scope){
29764         var dh = Roo.DomHelper;
29765         if(!this.footer){
29766             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29767         }
29768         if(!this.btnContainer){
29769             var tb = this.footer.createChild({
29770
29771                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29772                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29773             }, null, true);
29774             this.btnContainer = tb.firstChild.firstChild.firstChild;
29775         }
29776         var bconfig = {
29777             handler: handler,
29778             scope: scope,
29779             minWidth: this.minButtonWidth,
29780             hideParent:true
29781         };
29782         if(typeof config == "string"){
29783             bconfig.text = config;
29784         }else{
29785             if(config.tag){
29786                 bconfig.dhconfig = config;
29787             }else{
29788                 Roo.apply(bconfig, config);
29789             }
29790         }
29791         var fc = false;
29792         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29793             bconfig.position = Math.max(0, bconfig.position);
29794             fc = this.btnContainer.childNodes[bconfig.position];
29795         }
29796          
29797         var btn = new Roo.Button(
29798             fc ? 
29799                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29800                 : this.btnContainer.appendChild(document.createElement("td")),
29801             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29802             bconfig
29803         );
29804         this.syncBodyHeight();
29805         if(!this.buttons){
29806             /**
29807              * Array of all the buttons that have been added to this dialog via addButton
29808              * @type Array
29809              */
29810             this.buttons = [];
29811         }
29812         this.buttons.push(btn);
29813         return btn;
29814     },
29815
29816     /**
29817      * Sets the default button to be focused when the dialog is displayed.
29818      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29819      * @return {Roo.BasicDialog} this
29820      */
29821     setDefaultButton : function(btn){
29822         this.defaultButton = btn;
29823         return this;
29824     },
29825
29826     // private
29827     getHeaderFooterHeight : function(safe){
29828         var height = 0;
29829         if(this.header){
29830            height += this.header.getHeight();
29831         }
29832         if(this.footer){
29833            var fm = this.footer.getMargins();
29834             height += (this.footer.getHeight()+fm.top+fm.bottom);
29835         }
29836         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29837         height += this.centerBg.getPadding("tb");
29838         return height;
29839     },
29840
29841     // private
29842     syncBodyHeight : function(){
29843         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29844         var height = this.size.height - this.getHeaderFooterHeight(false);
29845         bd.setHeight(height-bd.getMargins("tb"));
29846         var hh = this.header.getHeight();
29847         var h = this.size.height-hh;
29848         cb.setHeight(h);
29849         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29850         bw.setHeight(h-cb.getPadding("tb"));
29851         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29852         bd.setWidth(bw.getWidth(true));
29853         if(this.tabs){
29854             this.tabs.syncHeight();
29855             if(Roo.isIE){
29856                 this.tabs.el.repaint();
29857             }
29858         }
29859     },
29860
29861     /**
29862      * Restores the previous state of the dialog if Roo.state is configured.
29863      * @return {Roo.BasicDialog} this
29864      */
29865     restoreState : function(){
29866         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29867         if(box && box.width){
29868             this.xy = [box.x, box.y];
29869             this.resizeTo(box.width, box.height);
29870         }
29871         return this;
29872     },
29873
29874     // private
29875     beforeShow : function(){
29876         this.expand();
29877         if(this.fixedcenter){
29878             this.xy = this.el.getCenterXY(true);
29879         }
29880         if(this.modal){
29881             Roo.get(document.body).addClass("x-body-masked");
29882             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29883             this.mask.show();
29884         }
29885         this.constrainXY();
29886     },
29887
29888     // private
29889     animShow : function(){
29890         var b = Roo.get(this.animateTarget).getBox();
29891         this.proxy.setSize(b.width, b.height);
29892         this.proxy.setLocation(b.x, b.y);
29893         this.proxy.show();
29894         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29895                     true, .35, this.showEl.createDelegate(this));
29896     },
29897
29898     /**
29899      * Shows the dialog.
29900      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29901      * @return {Roo.BasicDialog} this
29902      */
29903     show : function(animateTarget){
29904         if (this.fireEvent("beforeshow", this) === false){
29905             return;
29906         }
29907         if(this.syncHeightBeforeShow){
29908             this.syncBodyHeight();
29909         }else if(this.firstShow){
29910             this.firstShow = false;
29911             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29912         }
29913         this.animateTarget = animateTarget || this.animateTarget;
29914         if(!this.el.isVisible()){
29915             this.beforeShow();
29916             if(this.animateTarget && Roo.get(this.animateTarget)){
29917                 this.animShow();
29918             }else{
29919                 this.showEl();
29920             }
29921         }
29922         return this;
29923     },
29924
29925     // private
29926     showEl : function(){
29927         this.proxy.hide();
29928         this.el.setXY(this.xy);
29929         this.el.show();
29930         this.adjustAssets(true);
29931         this.toFront();
29932         this.focus();
29933         // IE peekaboo bug - fix found by Dave Fenwick
29934         if(Roo.isIE){
29935             this.el.repaint();
29936         }
29937         this.fireEvent("show", this);
29938     },
29939
29940     /**
29941      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29942      * dialog itself will receive focus.
29943      */
29944     focus : function(){
29945         if(this.defaultButton){
29946             this.defaultButton.focus();
29947         }else{
29948             this.focusEl.focus();
29949         }
29950     },
29951
29952     // private
29953     constrainXY : function(){
29954         if(this.constraintoviewport !== false){
29955             if(!this.viewSize){
29956                 if(this.container){
29957                     var s = this.container.getSize();
29958                     this.viewSize = [s.width, s.height];
29959                 }else{
29960                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29961                 }
29962             }
29963             var s = Roo.get(this.container||document).getScroll();
29964
29965             var x = this.xy[0], y = this.xy[1];
29966             var w = this.size.width, h = this.size.height;
29967             var vw = this.viewSize[0], vh = this.viewSize[1];
29968             // only move it if it needs it
29969             var moved = false;
29970             // first validate right/bottom
29971             if(x + w > vw+s.left){
29972                 x = vw - w;
29973                 moved = true;
29974             }
29975             if(y + h > vh+s.top){
29976                 y = vh - h;
29977                 moved = true;
29978             }
29979             // then make sure top/left isn't negative
29980             if(x < s.left){
29981                 x = s.left;
29982                 moved = true;
29983             }
29984             if(y < s.top){
29985                 y = s.top;
29986                 moved = true;
29987             }
29988             if(moved){
29989                 // cache xy
29990                 this.xy = [x, y];
29991                 if(this.isVisible()){
29992                     this.el.setLocation(x, y);
29993                     this.adjustAssets();
29994                 }
29995             }
29996         }
29997     },
29998
29999     // private
30000     onDrag : function(){
30001         if(!this.proxyDrag){
30002             this.xy = this.el.getXY();
30003             this.adjustAssets();
30004         }
30005     },
30006
30007     // private
30008     adjustAssets : function(doShow){
30009         var x = this.xy[0], y = this.xy[1];
30010         var w = this.size.width, h = this.size.height;
30011         if(doShow === true){
30012             if(this.shadow){
30013                 this.shadow.show(this.el);
30014             }
30015             if(this.shim){
30016                 this.shim.show();
30017             }
30018         }
30019         if(this.shadow && this.shadow.isVisible()){
30020             this.shadow.show(this.el);
30021         }
30022         if(this.shim && this.shim.isVisible()){
30023             this.shim.setBounds(x, y, w, h);
30024         }
30025     },
30026
30027     // private
30028     adjustViewport : function(w, h){
30029         if(!w || !h){
30030             w = Roo.lib.Dom.getViewWidth();
30031             h = Roo.lib.Dom.getViewHeight();
30032         }
30033         // cache the size
30034         this.viewSize = [w, h];
30035         if(this.modal && this.mask.isVisible()){
30036             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30037             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30038         }
30039         if(this.isVisible()){
30040             this.constrainXY();
30041         }
30042     },
30043
30044     /**
30045      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30046      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30047      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30048      */
30049     destroy : function(removeEl){
30050         if(this.isVisible()){
30051             this.animateTarget = null;
30052             this.hide();
30053         }
30054         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30055         if(this.tabs){
30056             this.tabs.destroy(removeEl);
30057         }
30058         Roo.destroy(
30059              this.shim,
30060              this.proxy,
30061              this.resizer,
30062              this.close,
30063              this.mask
30064         );
30065         if(this.dd){
30066             this.dd.unreg();
30067         }
30068         if(this.buttons){
30069            for(var i = 0, len = this.buttons.length; i < len; i++){
30070                this.buttons[i].destroy();
30071            }
30072         }
30073         this.el.removeAllListeners();
30074         if(removeEl === true){
30075             this.el.update("");
30076             this.el.remove();
30077         }
30078         Roo.DialogManager.unregister(this);
30079     },
30080
30081     // private
30082     startMove : function(){
30083         if(this.proxyDrag){
30084             this.proxy.show();
30085         }
30086         if(this.constraintoviewport !== false){
30087             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30088         }
30089     },
30090
30091     // private
30092     endMove : function(){
30093         if(!this.proxyDrag){
30094             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30095         }else{
30096             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30097             this.proxy.hide();
30098         }
30099         this.refreshSize();
30100         this.adjustAssets();
30101         this.focus();
30102         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30103     },
30104
30105     /**
30106      * Brings this dialog to the front of any other visible dialogs
30107      * @return {Roo.BasicDialog} this
30108      */
30109     toFront : function(){
30110         Roo.DialogManager.bringToFront(this);
30111         return this;
30112     },
30113
30114     /**
30115      * Sends this dialog to the back (under) of any other visible dialogs
30116      * @return {Roo.BasicDialog} this
30117      */
30118     toBack : function(){
30119         Roo.DialogManager.sendToBack(this);
30120         return this;
30121     },
30122
30123     /**
30124      * Centers this dialog in the viewport
30125      * @return {Roo.BasicDialog} this
30126      */
30127     center : function(){
30128         var xy = this.el.getCenterXY(true);
30129         this.moveTo(xy[0], xy[1]);
30130         return this;
30131     },
30132
30133     /**
30134      * Moves the dialog's top-left corner to the specified point
30135      * @param {Number} x
30136      * @param {Number} y
30137      * @return {Roo.BasicDialog} this
30138      */
30139     moveTo : function(x, y){
30140         this.xy = [x,y];
30141         if(this.isVisible()){
30142             this.el.setXY(this.xy);
30143             this.adjustAssets();
30144         }
30145         return this;
30146     },
30147
30148     /**
30149      * Aligns the dialog to the specified element
30150      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30151      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30152      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30153      * @return {Roo.BasicDialog} this
30154      */
30155     alignTo : function(element, position, offsets){
30156         this.xy = this.el.getAlignToXY(element, position, offsets);
30157         if(this.isVisible()){
30158             this.el.setXY(this.xy);
30159             this.adjustAssets();
30160         }
30161         return this;
30162     },
30163
30164     /**
30165      * Anchors an element to another element and realigns it when the window is resized.
30166      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30167      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30168      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30169      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30170      * is a number, it is used as the buffer delay (defaults to 50ms).
30171      * @return {Roo.BasicDialog} this
30172      */
30173     anchorTo : function(el, alignment, offsets, monitorScroll){
30174         var action = function(){
30175             this.alignTo(el, alignment, offsets);
30176         };
30177         Roo.EventManager.onWindowResize(action, this);
30178         var tm = typeof monitorScroll;
30179         if(tm != 'undefined'){
30180             Roo.EventManager.on(window, 'scroll', action, this,
30181                 {buffer: tm == 'number' ? monitorScroll : 50});
30182         }
30183         action.call(this);
30184         return this;
30185     },
30186
30187     /**
30188      * Returns true if the dialog is visible
30189      * @return {Boolean}
30190      */
30191     isVisible : function(){
30192         return this.el.isVisible();
30193     },
30194
30195     // private
30196     animHide : function(callback){
30197         var b = Roo.get(this.animateTarget).getBox();
30198         this.proxy.show();
30199         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30200         this.el.hide();
30201         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30202                     this.hideEl.createDelegate(this, [callback]));
30203     },
30204
30205     /**
30206      * Hides the dialog.
30207      * @param {Function} callback (optional) Function to call when the dialog is hidden
30208      * @return {Roo.BasicDialog} this
30209      */
30210     hide : function(callback){
30211         if (this.fireEvent("beforehide", this) === false){
30212             return;
30213         }
30214         if(this.shadow){
30215             this.shadow.hide();
30216         }
30217         if(this.shim) {
30218           this.shim.hide();
30219         }
30220         // sometimes animateTarget seems to get set.. causing problems...
30221         // this just double checks..
30222         if(this.animateTarget && Roo.get(this.animateTarget)) {
30223            this.animHide(callback);
30224         }else{
30225             this.el.hide();
30226             this.hideEl(callback);
30227         }
30228         return this;
30229     },
30230
30231     // private
30232     hideEl : function(callback){
30233         this.proxy.hide();
30234         if(this.modal){
30235             this.mask.hide();
30236             Roo.get(document.body).removeClass("x-body-masked");
30237         }
30238         this.fireEvent("hide", this);
30239         if(typeof callback == "function"){
30240             callback();
30241         }
30242     },
30243
30244     // private
30245     hideAction : function(){
30246         this.setLeft("-10000px");
30247         this.setTop("-10000px");
30248         this.setStyle("visibility", "hidden");
30249     },
30250
30251     // private
30252     refreshSize : function(){
30253         this.size = this.el.getSize();
30254         this.xy = this.el.getXY();
30255         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30256     },
30257
30258     // private
30259     // z-index is managed by the DialogManager and may be overwritten at any time
30260     setZIndex : function(index){
30261         if(this.modal){
30262             this.mask.setStyle("z-index", index);
30263         }
30264         if(this.shim){
30265             this.shim.setStyle("z-index", ++index);
30266         }
30267         if(this.shadow){
30268             this.shadow.setZIndex(++index);
30269         }
30270         this.el.setStyle("z-index", ++index);
30271         if(this.proxy){
30272             this.proxy.setStyle("z-index", ++index);
30273         }
30274         if(this.resizer){
30275             this.resizer.proxy.setStyle("z-index", ++index);
30276         }
30277
30278         this.lastZIndex = index;
30279     },
30280
30281     /**
30282      * Returns the element for this dialog
30283      * @return {Roo.Element} The underlying dialog Element
30284      */
30285     getEl : function(){
30286         return this.el;
30287     }
30288 });
30289
30290 /**
30291  * @class Roo.DialogManager
30292  * Provides global access to BasicDialogs that have been created and
30293  * support for z-indexing (layering) multiple open dialogs.
30294  */
30295 Roo.DialogManager = function(){
30296     var list = {};
30297     var accessList = [];
30298     var front = null;
30299
30300     // private
30301     var sortDialogs = function(d1, d2){
30302         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30303     };
30304
30305     // private
30306     var orderDialogs = function(){
30307         accessList.sort(sortDialogs);
30308         var seed = Roo.DialogManager.zseed;
30309         for(var i = 0, len = accessList.length; i < len; i++){
30310             var dlg = accessList[i];
30311             if(dlg){
30312                 dlg.setZIndex(seed + (i*10));
30313             }
30314         }
30315     };
30316
30317     return {
30318         /**
30319          * The starting z-index for BasicDialogs (defaults to 9000)
30320          * @type Number The z-index value
30321          */
30322         zseed : 9000,
30323
30324         // private
30325         register : function(dlg){
30326             list[dlg.id] = dlg;
30327             accessList.push(dlg);
30328         },
30329
30330         // private
30331         unregister : function(dlg){
30332             delete list[dlg.id];
30333             var i=0;
30334             var len=0;
30335             if(!accessList.indexOf){
30336                 for(  i = 0, len = accessList.length; i < len; i++){
30337                     if(accessList[i] == dlg){
30338                         accessList.splice(i, 1);
30339                         return;
30340                     }
30341                 }
30342             }else{
30343                  i = accessList.indexOf(dlg);
30344                 if(i != -1){
30345                     accessList.splice(i, 1);
30346                 }
30347             }
30348         },
30349
30350         /**
30351          * Gets a registered dialog by id
30352          * @param {String/Object} id The id of the dialog or a dialog
30353          * @return {Roo.BasicDialog} this
30354          */
30355         get : function(id){
30356             return typeof id == "object" ? id : list[id];
30357         },
30358
30359         /**
30360          * Brings the specified dialog to the front
30361          * @param {String/Object} dlg The id of the dialog or a dialog
30362          * @return {Roo.BasicDialog} this
30363          */
30364         bringToFront : function(dlg){
30365             dlg = this.get(dlg);
30366             if(dlg != front){
30367                 front = dlg;
30368                 dlg._lastAccess = new Date().getTime();
30369                 orderDialogs();
30370             }
30371             return dlg;
30372         },
30373
30374         /**
30375          * Sends the specified dialog to the back
30376          * @param {String/Object} dlg The id of the dialog or a dialog
30377          * @return {Roo.BasicDialog} this
30378          */
30379         sendToBack : function(dlg){
30380             dlg = this.get(dlg);
30381             dlg._lastAccess = -(new Date().getTime());
30382             orderDialogs();
30383             return dlg;
30384         },
30385
30386         /**
30387          * Hides all dialogs
30388          */
30389         hideAll : function(){
30390             for(var id in list){
30391                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
30392                     list[id].hide();
30393                 }
30394             }
30395         }
30396     };
30397 }();
30398
30399 /**
30400  * @class Roo.LayoutDialog
30401  * @extends Roo.BasicDialog
30402  * Dialog which provides adjustments for working with a layout in a Dialog.
30403  * Add your necessary layout config options to the dialog's config.<br>
30404  * Example usage (including a nested layout):
30405  * <pre><code>
30406 if(!dialog){
30407     dialog = new Roo.LayoutDialog("download-dlg", {
30408         modal: true,
30409         width:600,
30410         height:450,
30411         shadow:true,
30412         minWidth:500,
30413         minHeight:350,
30414         autoTabs:true,
30415         proxyDrag:true,
30416         // layout config merges with the dialog config
30417         center:{
30418             tabPosition: "top",
30419             alwaysShowTabs: true
30420         }
30421     });
30422     dialog.addKeyListener(27, dialog.hide, dialog);
30423     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
30424     dialog.addButton("Build It!", this.getDownload, this);
30425
30426     // we can even add nested layouts
30427     var innerLayout = new Roo.BorderLayout("dl-inner", {
30428         east: {
30429             initialSize: 200,
30430             autoScroll:true,
30431             split:true
30432         },
30433         center: {
30434             autoScroll:true
30435         }
30436     });
30437     innerLayout.beginUpdate();
30438     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
30439     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
30440     innerLayout.endUpdate(true);
30441
30442     var layout = dialog.getLayout();
30443     layout.beginUpdate();
30444     layout.add("center", new Roo.ContentPanel("standard-panel",
30445                         {title: "Download the Source", fitToFrame:true}));
30446     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
30447                {title: "Build your own roo.js"}));
30448     layout.getRegion("center").showPanel(sp);
30449     layout.endUpdate();
30450 }
30451 </code></pre>
30452     * @constructor
30453     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
30454     * @param {Object} config configuration options
30455   */
30456 Roo.LayoutDialog = function(el, cfg){
30457     
30458     var config=  cfg;
30459     if (typeof(cfg) == 'undefined') {
30460         config = Roo.apply({}, el);
30461         // not sure why we use documentElement here.. - it should always be body.
30462         // IE7 borks horribly if we use documentElement.
30463         // webkit also does not like documentElement - it creates a body element...
30464         el = Roo.get( document.body || document.documentElement ).createChild();
30465         //config.autoCreate = true;
30466     }
30467     
30468     
30469     config.autoTabs = false;
30470     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
30471     this.body.setStyle({overflow:"hidden", position:"relative"});
30472     this.layout = new Roo.BorderLayout(this.body.dom, config);
30473     this.layout.monitorWindowResize = false;
30474     this.el.addClass("x-dlg-auto-layout");
30475     // fix case when center region overwrites center function
30476     this.center = Roo.BasicDialog.prototype.center;
30477     this.on("show", this.layout.layout, this.layout, true);
30478     if (config.items) {
30479         var xitems = config.items;
30480         delete config.items;
30481         Roo.each(xitems, this.addxtype, this);
30482     }
30483     
30484     
30485 };
30486 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
30487     /**
30488      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
30489      * @deprecated
30490      */
30491     endUpdate : function(){
30492         this.layout.endUpdate();
30493     },
30494
30495     /**
30496      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
30497      *  @deprecated
30498      */
30499     beginUpdate : function(){
30500         this.layout.beginUpdate();
30501     },
30502
30503     /**
30504      * Get the BorderLayout for this dialog
30505      * @return {Roo.BorderLayout}
30506      */
30507     getLayout : function(){
30508         return this.layout;
30509     },
30510
30511     showEl : function(){
30512         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
30513         if(Roo.isIE7){
30514             this.layout.layout();
30515         }
30516     },
30517
30518     // private
30519     // Use the syncHeightBeforeShow config option to control this automatically
30520     syncBodyHeight : function(){
30521         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
30522         if(this.layout){this.layout.layout();}
30523     },
30524     
30525       /**
30526      * Add an xtype element (actually adds to the layout.)
30527      * @return {Object} xdata xtype object data.
30528      */
30529     
30530     addxtype : function(c) {
30531         return this.layout.addxtype(c);
30532     }
30533 });/*
30534  * Based on:
30535  * Ext JS Library 1.1.1
30536  * Copyright(c) 2006-2007, Ext JS, LLC.
30537  *
30538  * Originally Released Under LGPL - original licence link has changed is not relivant.
30539  *
30540  * Fork - LGPL
30541  * <script type="text/javascript">
30542  */
30543  
30544 /**
30545  * @class Roo.MessageBox
30546  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
30547  * Example usage:
30548  *<pre><code>
30549 // Basic alert:
30550 Roo.Msg.alert('Status', 'Changes saved successfully.');
30551
30552 // Prompt for user data:
30553 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
30554     if (btn == 'ok'){
30555         // process text value...
30556     }
30557 });
30558
30559 // Show a dialog using config options:
30560 Roo.Msg.show({
30561    title:'Save Changes?',
30562    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
30563    buttons: Roo.Msg.YESNOCANCEL,
30564    fn: processResult,
30565    animEl: 'elId'
30566 });
30567 </code></pre>
30568  * @singleton
30569  */
30570 Roo.MessageBox = function(){
30571     var dlg, opt, mask, waitTimer;
30572     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30573     var buttons, activeTextEl, bwidth;
30574
30575     // private
30576     var handleButton = function(button){
30577         dlg.hide();
30578         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30579     };
30580
30581     // private
30582     var handleHide = function(){
30583         if(opt && opt.cls){
30584             dlg.el.removeClass(opt.cls);
30585         }
30586         if(waitTimer){
30587             Roo.TaskMgr.stop(waitTimer);
30588             waitTimer = null;
30589         }
30590     };
30591
30592     // private
30593     var updateButtons = function(b){
30594         var width = 0;
30595         if(!b){
30596             buttons["ok"].hide();
30597             buttons["cancel"].hide();
30598             buttons["yes"].hide();
30599             buttons["no"].hide();
30600             dlg.footer.dom.style.display = 'none';
30601             return width;
30602         }
30603         dlg.footer.dom.style.display = '';
30604         for(var k in buttons){
30605             if(typeof buttons[k] != "function"){
30606                 if(b[k]){
30607                     buttons[k].show();
30608                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30609                     width += buttons[k].el.getWidth()+15;
30610                 }else{
30611                     buttons[k].hide();
30612                 }
30613             }
30614         }
30615         return width;
30616     };
30617
30618     // private
30619     var handleEsc = function(d, k, e){
30620         if(opt && opt.closable !== false){
30621             dlg.hide();
30622         }
30623         if(e){
30624             e.stopEvent();
30625         }
30626     };
30627
30628     return {
30629         /**
30630          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30631          * @return {Roo.BasicDialog} The BasicDialog element
30632          */
30633         getDialog : function(){
30634            if(!dlg){
30635                 dlg = new Roo.BasicDialog("x-msg-box", {
30636                     autoCreate : true,
30637                     shadow: true,
30638                     draggable: true,
30639                     resizable:false,
30640                     constraintoviewport:false,
30641                     fixedcenter:true,
30642                     collapsible : false,
30643                     shim:true,
30644                     modal: true,
30645                     width:400, height:100,
30646                     buttonAlign:"center",
30647                     closeClick : function(){
30648                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30649                             handleButton("no");
30650                         }else{
30651                             handleButton("cancel");
30652                         }
30653                     }
30654                 });
30655                 dlg.on("hide", handleHide);
30656                 mask = dlg.mask;
30657                 dlg.addKeyListener(27, handleEsc);
30658                 buttons = {};
30659                 var bt = this.buttonText;
30660                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30661                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30662                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30663                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30664                 bodyEl = dlg.body.createChild({
30665
30666                     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>'
30667                 });
30668                 msgEl = bodyEl.dom.firstChild;
30669                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30670                 textboxEl.enableDisplayMode();
30671                 textboxEl.addKeyListener([10,13], function(){
30672                     if(dlg.isVisible() && opt && opt.buttons){
30673                         if(opt.buttons.ok){
30674                             handleButton("ok");
30675                         }else if(opt.buttons.yes){
30676                             handleButton("yes");
30677                         }
30678                     }
30679                 });
30680                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30681                 textareaEl.enableDisplayMode();
30682                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30683                 progressEl.enableDisplayMode();
30684                 var pf = progressEl.dom.firstChild;
30685                 if (pf) {
30686                     pp = Roo.get(pf.firstChild);
30687                     pp.setHeight(pf.offsetHeight);
30688                 }
30689                 
30690             }
30691             return dlg;
30692         },
30693
30694         /**
30695          * Updates the message box body text
30696          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30697          * the XHTML-compliant non-breaking space character '&amp;#160;')
30698          * @return {Roo.MessageBox} This message box
30699          */
30700         updateText : function(text){
30701             if(!dlg.isVisible() && !opt.width){
30702                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30703             }
30704             msgEl.innerHTML = text || '&#160;';
30705       
30706             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30707             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30708             var w = Math.max(
30709                     Math.min(opt.width || cw , this.maxWidth), 
30710                     Math.max(opt.minWidth || this.minWidth, bwidth)
30711             );
30712             if(opt.prompt){
30713                 activeTextEl.setWidth(w);
30714             }
30715             if(dlg.isVisible()){
30716                 dlg.fixedcenter = false;
30717             }
30718             // to big, make it scroll. = But as usual stupid IE does not support
30719             // !important..
30720             
30721             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30722                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30723                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30724             } else {
30725                 bodyEl.dom.style.height = '';
30726                 bodyEl.dom.style.overflowY = '';
30727             }
30728             if (cw > w) {
30729                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30730             } else {
30731                 bodyEl.dom.style.overflowX = '';
30732             }
30733             
30734             dlg.setContentSize(w, bodyEl.getHeight());
30735             if(dlg.isVisible()){
30736                 dlg.fixedcenter = true;
30737             }
30738             return this;
30739         },
30740
30741         /**
30742          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30743          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30744          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30745          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30746          * @return {Roo.MessageBox} This message box
30747          */
30748         updateProgress : function(value, text){
30749             if(text){
30750                 this.updateText(text);
30751             }
30752             if (pp) { // weird bug on my firefox - for some reason this is not defined
30753                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30754             }
30755             return this;
30756         },        
30757
30758         /**
30759          * Returns true if the message box is currently displayed
30760          * @return {Boolean} True if the message box is visible, else false
30761          */
30762         isVisible : function(){
30763             return dlg && dlg.isVisible();  
30764         },
30765
30766         /**
30767          * Hides the message box if it is displayed
30768          */
30769         hide : function(){
30770             if(this.isVisible()){
30771                 dlg.hide();
30772             }  
30773         },
30774
30775         /**
30776          * Displays a new message box, or reinitializes an existing message box, based on the config options
30777          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30778          * The following config object properties are supported:
30779          * <pre>
30780 Property    Type             Description
30781 ----------  ---------------  ------------------------------------------------------------------------------------
30782 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30783                                    closes (defaults to undefined)
30784 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30785                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30786 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30787                                    progress and wait dialogs will ignore this property and always hide the
30788                                    close button as they can only be closed programmatically.
30789 cls               String           A custom CSS class to apply to the message box element
30790 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30791                                    displayed (defaults to 75)
30792 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30793                                    function will be btn (the name of the button that was clicked, if applicable,
30794                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30795                                    Progress and wait dialogs will ignore this option since they do not respond to
30796                                    user actions and can only be closed programmatically, so any required function
30797                                    should be called by the same code after it closes the dialog.
30798 icon              String           A CSS class that provides a background image to be used as an icon for
30799                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30800 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30801 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30802 modal             Boolean          False to allow user interaction with the page while the message box is
30803                                    displayed (defaults to true)
30804 msg               String           A string that will replace the existing message box body text (defaults
30805                                    to the XHTML-compliant non-breaking space character '&#160;')
30806 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30807 progress          Boolean          True to display a progress bar (defaults to false)
30808 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30809 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30810 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30811 title             String           The title text
30812 value             String           The string value to set into the active textbox element if displayed
30813 wait              Boolean          True to display a progress bar (defaults to false)
30814 width             Number           The width of the dialog in pixels
30815 </pre>
30816          *
30817          * Example usage:
30818          * <pre><code>
30819 Roo.Msg.show({
30820    title: 'Address',
30821    msg: 'Please enter your address:',
30822    width: 300,
30823    buttons: Roo.MessageBox.OKCANCEL,
30824    multiline: true,
30825    fn: saveAddress,
30826    animEl: 'addAddressBtn'
30827 });
30828 </code></pre>
30829          * @param {Object} config Configuration options
30830          * @return {Roo.MessageBox} This message box
30831          */
30832         show : function(options)
30833         {
30834             
30835             // this causes nightmares if you show one dialog after another
30836             // especially on callbacks..
30837              
30838             if(this.isVisible()){
30839                 
30840                 this.hide();
30841                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30842                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30843                 Roo.log("New Dialog Message:" +  options.msg )
30844                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30845                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30846                 
30847             }
30848             var d = this.getDialog();
30849             opt = options;
30850             d.setTitle(opt.title || "&#160;");
30851             d.close.setDisplayed(opt.closable !== false);
30852             activeTextEl = textboxEl;
30853             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30854             if(opt.prompt){
30855                 if(opt.multiline){
30856                     textboxEl.hide();
30857                     textareaEl.show();
30858                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30859                         opt.multiline : this.defaultTextHeight);
30860                     activeTextEl = textareaEl;
30861                 }else{
30862                     textboxEl.show();
30863                     textareaEl.hide();
30864                 }
30865             }else{
30866                 textboxEl.hide();
30867                 textareaEl.hide();
30868             }
30869             progressEl.setDisplayed(opt.progress === true);
30870             this.updateProgress(0);
30871             activeTextEl.dom.value = opt.value || "";
30872             if(opt.prompt){
30873                 dlg.setDefaultButton(activeTextEl);
30874             }else{
30875                 var bs = opt.buttons;
30876                 var db = null;
30877                 if(bs && bs.ok){
30878                     db = buttons["ok"];
30879                 }else if(bs && bs.yes){
30880                     db = buttons["yes"];
30881                 }
30882                 dlg.setDefaultButton(db);
30883             }
30884             bwidth = updateButtons(opt.buttons);
30885             this.updateText(opt.msg);
30886             if(opt.cls){
30887                 d.el.addClass(opt.cls);
30888             }
30889             d.proxyDrag = opt.proxyDrag === true;
30890             d.modal = opt.modal !== false;
30891             d.mask = opt.modal !== false ? mask : false;
30892             if(!d.isVisible()){
30893                 // force it to the end of the z-index stack so it gets a cursor in FF
30894                 document.body.appendChild(dlg.el.dom);
30895                 d.animateTarget = null;
30896                 d.show(options.animEl);
30897             }
30898             return this;
30899         },
30900
30901         /**
30902          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30903          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30904          * and closing the message box when the process is complete.
30905          * @param {String} title The title bar text
30906          * @param {String} msg The message box body text
30907          * @return {Roo.MessageBox} This message box
30908          */
30909         progress : function(title, msg){
30910             this.show({
30911                 title : title,
30912                 msg : msg,
30913                 buttons: false,
30914                 progress:true,
30915                 closable:false,
30916                 minWidth: this.minProgressWidth,
30917                 modal : true
30918             });
30919             return this;
30920         },
30921
30922         /**
30923          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30924          * If a callback function is passed it will be called after the user clicks the button, and the
30925          * id of the button that was clicked will be passed as the only parameter to the callback
30926          * (could also be the top-right close button).
30927          * @param {String} title The title bar text
30928          * @param {String} msg The message box body text
30929          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30930          * @param {Object} scope (optional) The scope of the callback function
30931          * @return {Roo.MessageBox} This message box
30932          */
30933         alert : function(title, msg, fn, scope){
30934             this.show({
30935                 title : title,
30936                 msg : msg,
30937                 buttons: this.OK,
30938                 fn: fn,
30939                 scope : scope,
30940                 modal : true
30941             });
30942             return this;
30943         },
30944
30945         /**
30946          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30947          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30948          * You are responsible for closing the message box when the process is complete.
30949          * @param {String} msg The message box body text
30950          * @param {String} title (optional) The title bar text
30951          * @return {Roo.MessageBox} This message box
30952          */
30953         wait : function(msg, title){
30954             this.show({
30955                 title : title,
30956                 msg : msg,
30957                 buttons: false,
30958                 closable:false,
30959                 progress:true,
30960                 modal:true,
30961                 width:300,
30962                 wait:true
30963             });
30964             waitTimer = Roo.TaskMgr.start({
30965                 run: function(i){
30966                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30967                 },
30968                 interval: 1000
30969             });
30970             return this;
30971         },
30972
30973         /**
30974          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30975          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30976          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30977          * @param {String} title The title bar text
30978          * @param {String} msg The message box body text
30979          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30980          * @param {Object} scope (optional) The scope of the callback function
30981          * @return {Roo.MessageBox} This message box
30982          */
30983         confirm : function(title, msg, fn, scope){
30984             this.show({
30985                 title : title,
30986                 msg : msg,
30987                 buttons: this.YESNO,
30988                 fn: fn,
30989                 scope : scope,
30990                 modal : true
30991             });
30992             return this;
30993         },
30994
30995         /**
30996          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30997          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30998          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30999          * (could also be the top-right close button) and the text that was entered will be passed as the two
31000          * parameters to the callback.
31001          * @param {String} title The title bar text
31002          * @param {String} msg The message box body text
31003          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31004          * @param {Object} scope (optional) The scope of the callback function
31005          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31006          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31007          * @return {Roo.MessageBox} This message box
31008          */
31009         prompt : function(title, msg, fn, scope, multiline){
31010             this.show({
31011                 title : title,
31012                 msg : msg,
31013                 buttons: this.OKCANCEL,
31014                 fn: fn,
31015                 minWidth:250,
31016                 scope : scope,
31017                 prompt:true,
31018                 multiline: multiline,
31019                 modal : true
31020             });
31021             return this;
31022         },
31023
31024         /**
31025          * Button config that displays a single OK button
31026          * @type Object
31027          */
31028         OK : {ok:true},
31029         /**
31030          * Button config that displays Yes and No buttons
31031          * @type Object
31032          */
31033         YESNO : {yes:true, no:true},
31034         /**
31035          * Button config that displays OK and Cancel buttons
31036          * @type Object
31037          */
31038         OKCANCEL : {ok:true, cancel:true},
31039         /**
31040          * Button config that displays Yes, No and Cancel buttons
31041          * @type Object
31042          */
31043         YESNOCANCEL : {yes:true, no:true, cancel:true},
31044
31045         /**
31046          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31047          * @type Number
31048          */
31049         defaultTextHeight : 75,
31050         /**
31051          * The maximum width in pixels of the message box (defaults to 600)
31052          * @type Number
31053          */
31054         maxWidth : 600,
31055         /**
31056          * The minimum width in pixels of the message box (defaults to 100)
31057          * @type Number
31058          */
31059         minWidth : 100,
31060         /**
31061          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31062          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31063          * @type Number
31064          */
31065         minProgressWidth : 250,
31066         /**
31067          * An object containing the default button text strings that can be overriden for localized language support.
31068          * Supported properties are: ok, cancel, yes and no.
31069          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31070          * @type Object
31071          */
31072         buttonText : {
31073             ok : "OK",
31074             cancel : "Cancel",
31075             yes : "Yes",
31076             no : "No"
31077         }
31078     };
31079 }();
31080
31081 /**
31082  * Shorthand for {@link Roo.MessageBox}
31083  */
31084 Roo.Msg = Roo.MessageBox;/*
31085  * Based on:
31086  * Ext JS Library 1.1.1
31087  * Copyright(c) 2006-2007, Ext JS, LLC.
31088  *
31089  * Originally Released Under LGPL - original licence link has changed is not relivant.
31090  *
31091  * Fork - LGPL
31092  * <script type="text/javascript">
31093  */
31094 /**
31095  * @class Roo.QuickTips
31096  * Provides attractive and customizable tooltips for any element.
31097  * @singleton
31098  */
31099 Roo.QuickTips = function(){
31100     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31101     var ce, bd, xy, dd;
31102     var visible = false, disabled = true, inited = false;
31103     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31104     
31105     var onOver = function(e){
31106         if(disabled){
31107             return;
31108         }
31109         var t = e.getTarget();
31110         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31111             return;
31112         }
31113         if(ce && t == ce.el){
31114             clearTimeout(hideProc);
31115             return;
31116         }
31117         if(t && tagEls[t.id]){
31118             tagEls[t.id].el = t;
31119             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31120             return;
31121         }
31122         var ttp, et = Roo.fly(t);
31123         var ns = cfg.namespace;
31124         if(tm.interceptTitles && t.title){
31125             ttp = t.title;
31126             t.qtip = ttp;
31127             t.removeAttribute("title");
31128             e.preventDefault();
31129         }else{
31130             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31131         }
31132         if(ttp){
31133             showProc = show.defer(tm.showDelay, tm, [{
31134                 el: t, 
31135                 text: ttp, 
31136                 width: et.getAttributeNS(ns, cfg.width),
31137                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31138                 title: et.getAttributeNS(ns, cfg.title),
31139                     cls: et.getAttributeNS(ns, cfg.cls)
31140             }]);
31141         }
31142     };
31143     
31144     var onOut = function(e){
31145         clearTimeout(showProc);
31146         var t = e.getTarget();
31147         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31148             hideProc = setTimeout(hide, tm.hideDelay);
31149         }
31150     };
31151     
31152     var onMove = function(e){
31153         if(disabled){
31154             return;
31155         }
31156         xy = e.getXY();
31157         xy[1] += 18;
31158         if(tm.trackMouse && ce){
31159             el.setXY(xy);
31160         }
31161     };
31162     
31163     var onDown = function(e){
31164         clearTimeout(showProc);
31165         clearTimeout(hideProc);
31166         if(!e.within(el)){
31167             if(tm.hideOnClick){
31168                 hide();
31169                 tm.disable();
31170                 tm.enable.defer(100, tm);
31171             }
31172         }
31173     };
31174     
31175     var getPad = function(){
31176         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31177     };
31178
31179     var show = function(o){
31180         if(disabled){
31181             return;
31182         }
31183         clearTimeout(dismissProc);
31184         ce = o;
31185         if(removeCls){ // in case manually hidden
31186             el.removeClass(removeCls);
31187             removeCls = null;
31188         }
31189         if(ce.cls){
31190             el.addClass(ce.cls);
31191             removeCls = ce.cls;
31192         }
31193         if(ce.title){
31194             tipTitle.update(ce.title);
31195             tipTitle.show();
31196         }else{
31197             tipTitle.update('');
31198             tipTitle.hide();
31199         }
31200         el.dom.style.width  = tm.maxWidth+'px';
31201         //tipBody.dom.style.width = '';
31202         tipBodyText.update(o.text);
31203         var p = getPad(), w = ce.width;
31204         if(!w){
31205             var td = tipBodyText.dom;
31206             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31207             if(aw > tm.maxWidth){
31208                 w = tm.maxWidth;
31209             }else if(aw < tm.minWidth){
31210                 w = tm.minWidth;
31211             }else{
31212                 w = aw;
31213             }
31214         }
31215         //tipBody.setWidth(w);
31216         el.setWidth(parseInt(w, 10) + p);
31217         if(ce.autoHide === false){
31218             close.setDisplayed(true);
31219             if(dd){
31220                 dd.unlock();
31221             }
31222         }else{
31223             close.setDisplayed(false);
31224             if(dd){
31225                 dd.lock();
31226             }
31227         }
31228         if(xy){
31229             el.avoidY = xy[1]-18;
31230             el.setXY(xy);
31231         }
31232         if(tm.animate){
31233             el.setOpacity(.1);
31234             el.setStyle("visibility", "visible");
31235             el.fadeIn({callback: afterShow});
31236         }else{
31237             afterShow();
31238         }
31239     };
31240     
31241     var afterShow = function(){
31242         if(ce){
31243             el.show();
31244             esc.enable();
31245             if(tm.autoDismiss && ce.autoHide !== false){
31246                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31247             }
31248         }
31249     };
31250     
31251     var hide = function(noanim){
31252         clearTimeout(dismissProc);
31253         clearTimeout(hideProc);
31254         ce = null;
31255         if(el.isVisible()){
31256             esc.disable();
31257             if(noanim !== true && tm.animate){
31258                 el.fadeOut({callback: afterHide});
31259             }else{
31260                 afterHide();
31261             } 
31262         }
31263     };
31264     
31265     var afterHide = function(){
31266         el.hide();
31267         if(removeCls){
31268             el.removeClass(removeCls);
31269             removeCls = null;
31270         }
31271     };
31272     
31273     return {
31274         /**
31275         * @cfg {Number} minWidth
31276         * The minimum width of the quick tip (defaults to 40)
31277         */
31278        minWidth : 40,
31279         /**
31280         * @cfg {Number} maxWidth
31281         * The maximum width of the quick tip (defaults to 300)
31282         */
31283        maxWidth : 300,
31284         /**
31285         * @cfg {Boolean} interceptTitles
31286         * True to automatically use the element's DOM title value if available (defaults to false)
31287         */
31288        interceptTitles : false,
31289         /**
31290         * @cfg {Boolean} trackMouse
31291         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31292         */
31293        trackMouse : false,
31294         /**
31295         * @cfg {Boolean} hideOnClick
31296         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31297         */
31298        hideOnClick : true,
31299         /**
31300         * @cfg {Number} showDelay
31301         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31302         */
31303        showDelay : 500,
31304         /**
31305         * @cfg {Number} hideDelay
31306         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31307         */
31308        hideDelay : 200,
31309         /**
31310         * @cfg {Boolean} autoHide
31311         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31312         * Used in conjunction with hideDelay.
31313         */
31314        autoHide : true,
31315         /**
31316         * @cfg {Boolean}
31317         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31318         * (defaults to true).  Used in conjunction with autoDismissDelay.
31319         */
31320        autoDismiss : true,
31321         /**
31322         * @cfg {Number}
31323         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31324         */
31325        autoDismissDelay : 5000,
31326        /**
31327         * @cfg {Boolean} animate
31328         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31329         */
31330        animate : false,
31331
31332        /**
31333         * @cfg {String} title
31334         * Title text to display (defaults to '').  This can be any valid HTML markup.
31335         */
31336         title: '',
31337        /**
31338         * @cfg {String} text
31339         * Body text to display (defaults to '').  This can be any valid HTML markup.
31340         */
31341         text : '',
31342        /**
31343         * @cfg {String} cls
31344         * A CSS class to apply to the base quick tip element (defaults to '').
31345         */
31346         cls : '',
31347        /**
31348         * @cfg {Number} width
31349         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
31350         * minWidth or maxWidth.
31351         */
31352         width : null,
31353
31354     /**
31355      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
31356      * or display QuickTips in a page.
31357      */
31358        init : function(){
31359           tm = Roo.QuickTips;
31360           cfg = tm.tagConfig;
31361           if(!inited){
31362               if(!Roo.isReady){ // allow calling of init() before onReady
31363                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
31364                   return;
31365               }
31366               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
31367               el.fxDefaults = {stopFx: true};
31368               // maximum custom styling
31369               //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>');
31370               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>');              
31371               tipTitle = el.child('h3');
31372               tipTitle.enableDisplayMode("block");
31373               tipBody = el.child('div.x-tip-bd');
31374               tipBodyText = el.child('div.x-tip-bd-inner');
31375               //bdLeft = el.child('div.x-tip-bd-left');
31376               //bdRight = el.child('div.x-tip-bd-right');
31377               close = el.child('div.x-tip-close');
31378               close.enableDisplayMode("block");
31379               close.on("click", hide);
31380               var d = Roo.get(document);
31381               d.on("mousedown", onDown);
31382               d.on("mouseover", onOver);
31383               d.on("mouseout", onOut);
31384               d.on("mousemove", onMove);
31385               esc = d.addKeyListener(27, hide);
31386               esc.disable();
31387               if(Roo.dd.DD){
31388                   dd = el.initDD("default", null, {
31389                       onDrag : function(){
31390                           el.sync();  
31391                       }
31392                   });
31393                   dd.setHandleElId(tipTitle.id);
31394                   dd.lock();
31395               }
31396               inited = true;
31397           }
31398           this.enable(); 
31399        },
31400
31401     /**
31402      * Configures a new quick tip instance and assigns it to a target element.  The following config options
31403      * are supported:
31404      * <pre>
31405 Property    Type                   Description
31406 ----------  ---------------------  ------------------------------------------------------------------------
31407 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
31408      * </ul>
31409      * @param {Object} config The config object
31410      */
31411        register : function(config){
31412            var cs = config instanceof Array ? config : arguments;
31413            for(var i = 0, len = cs.length; i < len; i++) {
31414                var c = cs[i];
31415                var target = c.target;
31416                if(target){
31417                    if(target instanceof Array){
31418                        for(var j = 0, jlen = target.length; j < jlen; j++){
31419                            tagEls[target[j]] = c;
31420                        }
31421                    }else{
31422                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
31423                    }
31424                }
31425            }
31426        },
31427
31428     /**
31429      * Removes this quick tip from its element and destroys it.
31430      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
31431      */
31432        unregister : function(el){
31433            delete tagEls[Roo.id(el)];
31434        },
31435
31436     /**
31437      * Enable this quick tip.
31438      */
31439        enable : function(){
31440            if(inited && disabled){
31441                locks.pop();
31442                if(locks.length < 1){
31443                    disabled = false;
31444                }
31445            }
31446        },
31447
31448     /**
31449      * Disable this quick tip.
31450      */
31451        disable : function(){
31452           disabled = true;
31453           clearTimeout(showProc);
31454           clearTimeout(hideProc);
31455           clearTimeout(dismissProc);
31456           if(ce){
31457               hide(true);
31458           }
31459           locks.push(1);
31460        },
31461
31462     /**
31463      * Returns true if the quick tip is enabled, else false.
31464      */
31465        isEnabled : function(){
31466             return !disabled;
31467        },
31468
31469         // private
31470        tagConfig : {
31471            namespace : "ext",
31472            attribute : "qtip",
31473            width : "width",
31474            target : "target",
31475            title : "qtitle",
31476            hide : "hide",
31477            cls : "qclass"
31478        }
31479    };
31480 }();
31481
31482 // backwards compat
31483 Roo.QuickTips.tips = Roo.QuickTips.register;/*
31484  * Based on:
31485  * Ext JS Library 1.1.1
31486  * Copyright(c) 2006-2007, Ext JS, LLC.
31487  *
31488  * Originally Released Under LGPL - original licence link has changed is not relivant.
31489  *
31490  * Fork - LGPL
31491  * <script type="text/javascript">
31492  */
31493  
31494
31495 /**
31496  * @class Roo.tree.TreePanel
31497  * @extends Roo.data.Tree
31498
31499  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
31500  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
31501  * @cfg {Boolean} enableDD true to enable drag and drop
31502  * @cfg {Boolean} enableDrag true to enable just drag
31503  * @cfg {Boolean} enableDrop true to enable just drop
31504  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
31505  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
31506  * @cfg {String} ddGroup The DD group this TreePanel belongs to
31507  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
31508  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
31509  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
31510  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
31511  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
31512  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
31513  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
31514  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
31515  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
31516  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
31517  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
31518  * @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>
31519  * @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>
31520  * 
31521  * @constructor
31522  * @param {String/HTMLElement/Element} el The container element
31523  * @param {Object} config
31524  */
31525 Roo.tree.TreePanel = function(el, config){
31526     var root = false;
31527     var loader = false;
31528     if (config.root) {
31529         root = config.root;
31530         delete config.root;
31531     }
31532     if (config.loader) {
31533         loader = config.loader;
31534         delete config.loader;
31535     }
31536     
31537     Roo.apply(this, config);
31538     Roo.tree.TreePanel.superclass.constructor.call(this);
31539     this.el = Roo.get(el);
31540     this.el.addClass('x-tree');
31541     //console.log(root);
31542     if (root) {
31543         this.setRootNode( Roo.factory(root, Roo.tree));
31544     }
31545     if (loader) {
31546         this.loader = Roo.factory(loader, Roo.tree);
31547     }
31548    /**
31549     * Read-only. The id of the container element becomes this TreePanel's id.
31550     */
31551     this.id = this.el.id;
31552     this.addEvents({
31553         /**
31554         * @event beforeload
31555         * Fires before a node is loaded, return false to cancel
31556         * @param {Node} node The node being loaded
31557         */
31558         "beforeload" : true,
31559         /**
31560         * @event load
31561         * Fires when a node is loaded
31562         * @param {Node} node The node that was loaded
31563         */
31564         "load" : true,
31565         /**
31566         * @event textchange
31567         * Fires when the text for a node is changed
31568         * @param {Node} node The node
31569         * @param {String} text The new text
31570         * @param {String} oldText The old text
31571         */
31572         "textchange" : true,
31573         /**
31574         * @event beforeexpand
31575         * Fires before a node is expanded, return false to cancel.
31576         * @param {Node} node The node
31577         * @param {Boolean} deep
31578         * @param {Boolean} anim
31579         */
31580         "beforeexpand" : true,
31581         /**
31582         * @event beforecollapse
31583         * Fires before a node is collapsed, return false to cancel.
31584         * @param {Node} node The node
31585         * @param {Boolean} deep
31586         * @param {Boolean} anim
31587         */
31588         "beforecollapse" : true,
31589         /**
31590         * @event expand
31591         * Fires when a node is expanded
31592         * @param {Node} node The node
31593         */
31594         "expand" : true,
31595         /**
31596         * @event disabledchange
31597         * Fires when the disabled status of a node changes
31598         * @param {Node} node The node
31599         * @param {Boolean} disabled
31600         */
31601         "disabledchange" : true,
31602         /**
31603         * @event collapse
31604         * Fires when a node is collapsed
31605         * @param {Node} node The node
31606         */
31607         "collapse" : true,
31608         /**
31609         * @event beforeclick
31610         * Fires before click processing on a node. Return false to cancel the default action.
31611         * @param {Node} node The node
31612         * @param {Roo.EventObject} e The event object
31613         */
31614         "beforeclick":true,
31615         /**
31616         * @event checkchange
31617         * Fires when a node with a checkbox's checked property changes
31618         * @param {Node} this This node
31619         * @param {Boolean} checked
31620         */
31621         "checkchange":true,
31622         /**
31623         * @event click
31624         * Fires when a node is clicked
31625         * @param {Node} node The node
31626         * @param {Roo.EventObject} e The event object
31627         */
31628         "click":true,
31629         /**
31630         * @event dblclick
31631         * Fires when a node is double clicked
31632         * @param {Node} node The node
31633         * @param {Roo.EventObject} e The event object
31634         */
31635         "dblclick":true,
31636         /**
31637         * @event contextmenu
31638         * Fires when a node is right clicked
31639         * @param {Node} node The node
31640         * @param {Roo.EventObject} e The event object
31641         */
31642         "contextmenu":true,
31643         /**
31644         * @event beforechildrenrendered
31645         * Fires right before the child nodes for a node are rendered
31646         * @param {Node} node The node
31647         */
31648         "beforechildrenrendered":true,
31649         /**
31650         * @event startdrag
31651         * Fires when a node starts being dragged
31652         * @param {Roo.tree.TreePanel} this
31653         * @param {Roo.tree.TreeNode} node
31654         * @param {event} e The raw browser event
31655         */ 
31656        "startdrag" : true,
31657        /**
31658         * @event enddrag
31659         * Fires when a drag operation is complete
31660         * @param {Roo.tree.TreePanel} this
31661         * @param {Roo.tree.TreeNode} node
31662         * @param {event} e The raw browser event
31663         */
31664        "enddrag" : true,
31665        /**
31666         * @event dragdrop
31667         * Fires when a dragged node is dropped on a valid DD target
31668         * @param {Roo.tree.TreePanel} this
31669         * @param {Roo.tree.TreeNode} node
31670         * @param {DD} dd The dd it was dropped on
31671         * @param {event} e The raw browser event
31672         */
31673        "dragdrop" : true,
31674        /**
31675         * @event beforenodedrop
31676         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31677         * passed to handlers has the following properties:<br />
31678         * <ul style="padding:5px;padding-left:16px;">
31679         * <li>tree - The TreePanel</li>
31680         * <li>target - The node being targeted for the drop</li>
31681         * <li>data - The drag data from the drag source</li>
31682         * <li>point - The point of the drop - append, above or below</li>
31683         * <li>source - The drag source</li>
31684         * <li>rawEvent - Raw mouse event</li>
31685         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31686         * to be inserted by setting them on this object.</li>
31687         * <li>cancel - Set this to true to cancel the drop.</li>
31688         * </ul>
31689         * @param {Object} dropEvent
31690         */
31691        "beforenodedrop" : true,
31692        /**
31693         * @event nodedrop
31694         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31695         * passed to handlers has the following properties:<br />
31696         * <ul style="padding:5px;padding-left:16px;">
31697         * <li>tree - The TreePanel</li>
31698         * <li>target - The node being targeted for the drop</li>
31699         * <li>data - The drag data from the drag source</li>
31700         * <li>point - The point of the drop - append, above or below</li>
31701         * <li>source - The drag source</li>
31702         * <li>rawEvent - Raw mouse event</li>
31703         * <li>dropNode - Dropped node(s).</li>
31704         * </ul>
31705         * @param {Object} dropEvent
31706         */
31707        "nodedrop" : true,
31708         /**
31709         * @event nodedragover
31710         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31711         * passed to handlers has the following properties:<br />
31712         * <ul style="padding:5px;padding-left:16px;">
31713         * <li>tree - The TreePanel</li>
31714         * <li>target - The node being targeted for the drop</li>
31715         * <li>data - The drag data from the drag source</li>
31716         * <li>point - The point of the drop - append, above or below</li>
31717         * <li>source - The drag source</li>
31718         * <li>rawEvent - Raw mouse event</li>
31719         * <li>dropNode - Drop node(s) provided by the source.</li>
31720         * <li>cancel - Set this to true to signal drop not allowed.</li>
31721         * </ul>
31722         * @param {Object} dragOverEvent
31723         */
31724        "nodedragover" : true
31725         
31726     });
31727     if(this.singleExpand){
31728        this.on("beforeexpand", this.restrictExpand, this);
31729     }
31730     if (this.editor) {
31731         this.editor.tree = this;
31732         this.editor = Roo.factory(this.editor, Roo.tree);
31733     }
31734     
31735     if (this.selModel) {
31736         this.selModel = Roo.factory(this.selModel, Roo.tree);
31737     }
31738    
31739 };
31740 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31741     rootVisible : true,
31742     animate: Roo.enableFx,
31743     lines : true,
31744     enableDD : false,
31745     hlDrop : Roo.enableFx,
31746   
31747     renderer: false,
31748     
31749     rendererTip: false,
31750     // private
31751     restrictExpand : function(node){
31752         var p = node.parentNode;
31753         if(p){
31754             if(p.expandedChild && p.expandedChild.parentNode == p){
31755                 p.expandedChild.collapse();
31756             }
31757             p.expandedChild = node;
31758         }
31759     },
31760
31761     // private override
31762     setRootNode : function(node){
31763         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31764         if(!this.rootVisible){
31765             node.ui = new Roo.tree.RootTreeNodeUI(node);
31766         }
31767         return node;
31768     },
31769
31770     /**
31771      * Returns the container element for this TreePanel
31772      */
31773     getEl : function(){
31774         return this.el;
31775     },
31776
31777     /**
31778      * Returns the default TreeLoader for this TreePanel
31779      */
31780     getLoader : function(){
31781         return this.loader;
31782     },
31783
31784     /**
31785      * Expand all nodes
31786      */
31787     expandAll : function(){
31788         this.root.expand(true);
31789     },
31790
31791     /**
31792      * Collapse all nodes
31793      */
31794     collapseAll : function(){
31795         this.root.collapse(true);
31796     },
31797
31798     /**
31799      * Returns the selection model used by this TreePanel
31800      */
31801     getSelectionModel : function(){
31802         if(!this.selModel){
31803             this.selModel = new Roo.tree.DefaultSelectionModel();
31804         }
31805         return this.selModel;
31806     },
31807
31808     /**
31809      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31810      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31811      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31812      * @return {Array}
31813      */
31814     getChecked : function(a, startNode){
31815         startNode = startNode || this.root;
31816         var r = [];
31817         var f = function(){
31818             if(this.attributes.checked){
31819                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31820             }
31821         }
31822         startNode.cascade(f);
31823         return r;
31824     },
31825
31826     /**
31827      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31828      * @param {String} path
31829      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31830      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31831      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31832      */
31833     expandPath : function(path, attr, callback){
31834         attr = attr || "id";
31835         var keys = path.split(this.pathSeparator);
31836         var curNode = this.root;
31837         if(curNode.attributes[attr] != keys[1]){ // invalid root
31838             if(callback){
31839                 callback(false, null);
31840             }
31841             return;
31842         }
31843         var index = 1;
31844         var f = function(){
31845             if(++index == keys.length){
31846                 if(callback){
31847                     callback(true, curNode);
31848                 }
31849                 return;
31850             }
31851             var c = curNode.findChild(attr, keys[index]);
31852             if(!c){
31853                 if(callback){
31854                     callback(false, curNode);
31855                 }
31856                 return;
31857             }
31858             curNode = c;
31859             c.expand(false, false, f);
31860         };
31861         curNode.expand(false, false, f);
31862     },
31863
31864     /**
31865      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31866      * @param {String} path
31867      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31868      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31869      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31870      */
31871     selectPath : function(path, attr, callback){
31872         attr = attr || "id";
31873         var keys = path.split(this.pathSeparator);
31874         var v = keys.pop();
31875         if(keys.length > 0){
31876             var f = function(success, node){
31877                 if(success && node){
31878                     var n = node.findChild(attr, v);
31879                     if(n){
31880                         n.select();
31881                         if(callback){
31882                             callback(true, n);
31883                         }
31884                     }else if(callback){
31885                         callback(false, n);
31886                     }
31887                 }else{
31888                     if(callback){
31889                         callback(false, n);
31890                     }
31891                 }
31892             };
31893             this.expandPath(keys.join(this.pathSeparator), attr, f);
31894         }else{
31895             this.root.select();
31896             if(callback){
31897                 callback(true, this.root);
31898             }
31899         }
31900     },
31901
31902     getTreeEl : function(){
31903         return this.el;
31904     },
31905
31906     /**
31907      * Trigger rendering of this TreePanel
31908      */
31909     render : function(){
31910         if (this.innerCt) {
31911             return this; // stop it rendering more than once!!
31912         }
31913         
31914         this.innerCt = this.el.createChild({tag:"ul",
31915                cls:"x-tree-root-ct " +
31916                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31917
31918         if(this.containerScroll){
31919             Roo.dd.ScrollManager.register(this.el);
31920         }
31921         if((this.enableDD || this.enableDrop) && !this.dropZone){
31922            /**
31923             * The dropZone used by this tree if drop is enabled
31924             * @type Roo.tree.TreeDropZone
31925             */
31926              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31927                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31928            });
31929         }
31930         if((this.enableDD || this.enableDrag) && !this.dragZone){
31931            /**
31932             * The dragZone used by this tree if drag is enabled
31933             * @type Roo.tree.TreeDragZone
31934             */
31935             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31936                ddGroup: this.ddGroup || "TreeDD",
31937                scroll: this.ddScroll
31938            });
31939         }
31940         this.getSelectionModel().init(this);
31941         if (!this.root) {
31942             Roo.log("ROOT not set in tree");
31943             return this;
31944         }
31945         this.root.render();
31946         if(!this.rootVisible){
31947             this.root.renderChildren();
31948         }
31949         return this;
31950     }
31951 });/*
31952  * Based on:
31953  * Ext JS Library 1.1.1
31954  * Copyright(c) 2006-2007, Ext JS, LLC.
31955  *
31956  * Originally Released Under LGPL - original licence link has changed is not relivant.
31957  *
31958  * Fork - LGPL
31959  * <script type="text/javascript">
31960  */
31961  
31962
31963 /**
31964  * @class Roo.tree.DefaultSelectionModel
31965  * @extends Roo.util.Observable
31966  * The default single selection for a TreePanel.
31967  * @param {Object} cfg Configuration
31968  */
31969 Roo.tree.DefaultSelectionModel = function(cfg){
31970    this.selNode = null;
31971    
31972    
31973    
31974    this.addEvents({
31975        /**
31976         * @event selectionchange
31977         * Fires when the selected node changes
31978         * @param {DefaultSelectionModel} this
31979         * @param {TreeNode} node the new selection
31980         */
31981        "selectionchange" : true,
31982
31983        /**
31984         * @event beforeselect
31985         * Fires before the selected node changes, return false to cancel the change
31986         * @param {DefaultSelectionModel} this
31987         * @param {TreeNode} node the new selection
31988         * @param {TreeNode} node the old selection
31989         */
31990        "beforeselect" : true
31991    });
31992    
31993     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31994 };
31995
31996 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31997     init : function(tree){
31998         this.tree = tree;
31999         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32000         tree.on("click", this.onNodeClick, this);
32001     },
32002     
32003     onNodeClick : function(node, e){
32004         if (e.ctrlKey && this.selNode == node)  {
32005             this.unselect(node);
32006             return;
32007         }
32008         this.select(node);
32009     },
32010     
32011     /**
32012      * Select a node.
32013      * @param {TreeNode} node The node to select
32014      * @return {TreeNode} The selected node
32015      */
32016     select : function(node){
32017         var last = this.selNode;
32018         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32019             if(last){
32020                 last.ui.onSelectedChange(false);
32021             }
32022             this.selNode = node;
32023             node.ui.onSelectedChange(true);
32024             this.fireEvent("selectionchange", this, node, last);
32025         }
32026         return node;
32027     },
32028     
32029     /**
32030      * Deselect a node.
32031      * @param {TreeNode} node The node to unselect
32032      */
32033     unselect : function(node){
32034         if(this.selNode == node){
32035             this.clearSelections();
32036         }    
32037     },
32038     
32039     /**
32040      * Clear all selections
32041      */
32042     clearSelections : function(){
32043         var n = this.selNode;
32044         if(n){
32045             n.ui.onSelectedChange(false);
32046             this.selNode = null;
32047             this.fireEvent("selectionchange", this, null);
32048         }
32049         return n;
32050     },
32051     
32052     /**
32053      * Get the selected node
32054      * @return {TreeNode} The selected node
32055      */
32056     getSelectedNode : function(){
32057         return this.selNode;    
32058     },
32059     
32060     /**
32061      * Returns true if the node is selected
32062      * @param {TreeNode} node The node to check
32063      * @return {Boolean}
32064      */
32065     isSelected : function(node){
32066         return this.selNode == node;  
32067     },
32068
32069     /**
32070      * Selects the node above the selected node in the tree, intelligently walking the nodes
32071      * @return TreeNode The new selection
32072      */
32073     selectPrevious : function(){
32074         var s = this.selNode || this.lastSelNode;
32075         if(!s){
32076             return null;
32077         }
32078         var ps = s.previousSibling;
32079         if(ps){
32080             if(!ps.isExpanded() || ps.childNodes.length < 1){
32081                 return this.select(ps);
32082             } else{
32083                 var lc = ps.lastChild;
32084                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32085                     lc = lc.lastChild;
32086                 }
32087                 return this.select(lc);
32088             }
32089         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32090             return this.select(s.parentNode);
32091         }
32092         return null;
32093     },
32094
32095     /**
32096      * Selects the node above the selected node in the tree, intelligently walking the nodes
32097      * @return TreeNode The new selection
32098      */
32099     selectNext : function(){
32100         var s = this.selNode || this.lastSelNode;
32101         if(!s){
32102             return null;
32103         }
32104         if(s.firstChild && s.isExpanded()){
32105              return this.select(s.firstChild);
32106          }else if(s.nextSibling){
32107              return this.select(s.nextSibling);
32108          }else if(s.parentNode){
32109             var newS = null;
32110             s.parentNode.bubble(function(){
32111                 if(this.nextSibling){
32112                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32113                     return false;
32114                 }
32115             });
32116             return newS;
32117          }
32118         return null;
32119     },
32120
32121     onKeyDown : function(e){
32122         var s = this.selNode || this.lastSelNode;
32123         // undesirable, but required
32124         var sm = this;
32125         if(!s){
32126             return;
32127         }
32128         var k = e.getKey();
32129         switch(k){
32130              case e.DOWN:
32131                  e.stopEvent();
32132                  this.selectNext();
32133              break;
32134              case e.UP:
32135                  e.stopEvent();
32136                  this.selectPrevious();
32137              break;
32138              case e.RIGHT:
32139                  e.preventDefault();
32140                  if(s.hasChildNodes()){
32141                      if(!s.isExpanded()){
32142                          s.expand();
32143                      }else if(s.firstChild){
32144                          this.select(s.firstChild, e);
32145                      }
32146                  }
32147              break;
32148              case e.LEFT:
32149                  e.preventDefault();
32150                  if(s.hasChildNodes() && s.isExpanded()){
32151                      s.collapse();
32152                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32153                      this.select(s.parentNode, e);
32154                  }
32155              break;
32156         };
32157     }
32158 });
32159
32160 /**
32161  * @class Roo.tree.MultiSelectionModel
32162  * @extends Roo.util.Observable
32163  * Multi selection for a TreePanel.
32164  * @param {Object} cfg Configuration
32165  */
32166 Roo.tree.MultiSelectionModel = function(){
32167    this.selNodes = [];
32168    this.selMap = {};
32169    this.addEvents({
32170        /**
32171         * @event selectionchange
32172         * Fires when the selected nodes change
32173         * @param {MultiSelectionModel} this
32174         * @param {Array} nodes Array of the selected nodes
32175         */
32176        "selectionchange" : true
32177    });
32178    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32179    
32180 };
32181
32182 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32183     init : function(tree){
32184         this.tree = tree;
32185         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32186         tree.on("click", this.onNodeClick, this);
32187     },
32188     
32189     onNodeClick : function(node, e){
32190         this.select(node, e, e.ctrlKey);
32191     },
32192     
32193     /**
32194      * Select a node.
32195      * @param {TreeNode} node The node to select
32196      * @param {EventObject} e (optional) An event associated with the selection
32197      * @param {Boolean} keepExisting True to retain existing selections
32198      * @return {TreeNode} The selected node
32199      */
32200     select : function(node, e, keepExisting){
32201         if(keepExisting !== true){
32202             this.clearSelections(true);
32203         }
32204         if(this.isSelected(node)){
32205             this.lastSelNode = node;
32206             return node;
32207         }
32208         this.selNodes.push(node);
32209         this.selMap[node.id] = node;
32210         this.lastSelNode = node;
32211         node.ui.onSelectedChange(true);
32212         this.fireEvent("selectionchange", this, this.selNodes);
32213         return node;
32214     },
32215     
32216     /**
32217      * Deselect a node.
32218      * @param {TreeNode} node The node to unselect
32219      */
32220     unselect : function(node){
32221         if(this.selMap[node.id]){
32222             node.ui.onSelectedChange(false);
32223             var sn = this.selNodes;
32224             var index = -1;
32225             if(sn.indexOf){
32226                 index = sn.indexOf(node);
32227             }else{
32228                 for(var i = 0, len = sn.length; i < len; i++){
32229                     if(sn[i] == node){
32230                         index = i;
32231                         break;
32232                     }
32233                 }
32234             }
32235             if(index != -1){
32236                 this.selNodes.splice(index, 1);
32237             }
32238             delete this.selMap[node.id];
32239             this.fireEvent("selectionchange", this, this.selNodes);
32240         }
32241     },
32242     
32243     /**
32244      * Clear all selections
32245      */
32246     clearSelections : function(suppressEvent){
32247         var sn = this.selNodes;
32248         if(sn.length > 0){
32249             for(var i = 0, len = sn.length; i < len; i++){
32250                 sn[i].ui.onSelectedChange(false);
32251             }
32252             this.selNodes = [];
32253             this.selMap = {};
32254             if(suppressEvent !== true){
32255                 this.fireEvent("selectionchange", this, this.selNodes);
32256             }
32257         }
32258     },
32259     
32260     /**
32261      * Returns true if the node is selected
32262      * @param {TreeNode} node The node to check
32263      * @return {Boolean}
32264      */
32265     isSelected : function(node){
32266         return this.selMap[node.id] ? true : false;  
32267     },
32268     
32269     /**
32270      * Returns an array of the selected nodes
32271      * @return {Array}
32272      */
32273     getSelectedNodes : function(){
32274         return this.selNodes;    
32275     },
32276
32277     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32278
32279     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32280
32281     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32282 });/*
32283  * Based on:
32284  * Ext JS Library 1.1.1
32285  * Copyright(c) 2006-2007, Ext JS, LLC.
32286  *
32287  * Originally Released Under LGPL - original licence link has changed is not relivant.
32288  *
32289  * Fork - LGPL
32290  * <script type="text/javascript">
32291  */
32292  
32293 /**
32294  * @class Roo.tree.TreeNode
32295  * @extends Roo.data.Node
32296  * @cfg {String} text The text for this node
32297  * @cfg {Boolean} expanded true to start the node expanded
32298  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32299  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32300  * @cfg {Boolean} disabled true to start the node disabled
32301  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32302  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32303  * @cfg {String} cls A css class to be added to the node
32304  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32305  * @cfg {String} href URL of the link used for the node (defaults to #)
32306  * @cfg {String} hrefTarget target frame for the link
32307  * @cfg {String} qtip An Ext QuickTip for the node
32308  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32309  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32310  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32311  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32312  * (defaults to undefined with no checkbox rendered)
32313  * @constructor
32314  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32315  */
32316 Roo.tree.TreeNode = function(attributes){
32317     attributes = attributes || {};
32318     if(typeof attributes == "string"){
32319         attributes = {text: attributes};
32320     }
32321     this.childrenRendered = false;
32322     this.rendered = false;
32323     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32324     this.expanded = attributes.expanded === true;
32325     this.isTarget = attributes.isTarget !== false;
32326     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32327     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32328
32329     /**
32330      * Read-only. The text for this node. To change it use setText().
32331      * @type String
32332      */
32333     this.text = attributes.text;
32334     /**
32335      * True if this node is disabled.
32336      * @type Boolean
32337      */
32338     this.disabled = attributes.disabled === true;
32339
32340     this.addEvents({
32341         /**
32342         * @event textchange
32343         * Fires when the text for this node is changed
32344         * @param {Node} this This node
32345         * @param {String} text The new text
32346         * @param {String} oldText The old text
32347         */
32348         "textchange" : true,
32349         /**
32350         * @event beforeexpand
32351         * Fires before this node is expanded, return false to cancel.
32352         * @param {Node} this This node
32353         * @param {Boolean} deep
32354         * @param {Boolean} anim
32355         */
32356         "beforeexpand" : true,
32357         /**
32358         * @event beforecollapse
32359         * Fires before this node is collapsed, return false to cancel.
32360         * @param {Node} this This node
32361         * @param {Boolean} deep
32362         * @param {Boolean} anim
32363         */
32364         "beforecollapse" : true,
32365         /**
32366         * @event expand
32367         * Fires when this node is expanded
32368         * @param {Node} this This node
32369         */
32370         "expand" : true,
32371         /**
32372         * @event disabledchange
32373         * Fires when the disabled status of this node changes
32374         * @param {Node} this This node
32375         * @param {Boolean} disabled
32376         */
32377         "disabledchange" : true,
32378         /**
32379         * @event collapse
32380         * Fires when this node is collapsed
32381         * @param {Node} this This node
32382         */
32383         "collapse" : true,
32384         /**
32385         * @event beforeclick
32386         * Fires before click processing. Return false to cancel the default action.
32387         * @param {Node} this This node
32388         * @param {Roo.EventObject} e The event object
32389         */
32390         "beforeclick":true,
32391         /**
32392         * @event checkchange
32393         * Fires when a node with a checkbox's checked property changes
32394         * @param {Node} this This node
32395         * @param {Boolean} checked
32396         */
32397         "checkchange":true,
32398         /**
32399         * @event click
32400         * Fires when this node is clicked
32401         * @param {Node} this This node
32402         * @param {Roo.EventObject} e The event object
32403         */
32404         "click":true,
32405         /**
32406         * @event dblclick
32407         * Fires when this node is double clicked
32408         * @param {Node} this This node
32409         * @param {Roo.EventObject} e The event object
32410         */
32411         "dblclick":true,
32412         /**
32413         * @event contextmenu
32414         * Fires when this node is right clicked
32415         * @param {Node} this This node
32416         * @param {Roo.EventObject} e The event object
32417         */
32418         "contextmenu":true,
32419         /**
32420         * @event beforechildrenrendered
32421         * Fires right before the child nodes for this node are rendered
32422         * @param {Node} this This node
32423         */
32424         "beforechildrenrendered":true
32425     });
32426
32427     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
32428
32429     /**
32430      * Read-only. The UI for this node
32431      * @type TreeNodeUI
32432      */
32433     this.ui = new uiClass(this);
32434     
32435     // finally support items[]
32436     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
32437         return;
32438     }
32439     
32440     
32441     Roo.each(this.attributes.items, function(c) {
32442         this.appendChild(Roo.factory(c,Roo.Tree));
32443     }, this);
32444     delete this.attributes.items;
32445     
32446     
32447     
32448 };
32449 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
32450     preventHScroll: true,
32451     /**
32452      * Returns true if this node is expanded
32453      * @return {Boolean}
32454      */
32455     isExpanded : function(){
32456         return this.expanded;
32457     },
32458
32459     /**
32460      * Returns the UI object for this node
32461      * @return {TreeNodeUI}
32462      */
32463     getUI : function(){
32464         return this.ui;
32465     },
32466
32467     // private override
32468     setFirstChild : function(node){
32469         var of = this.firstChild;
32470         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
32471         if(this.childrenRendered && of && node != of){
32472             of.renderIndent(true, true);
32473         }
32474         if(this.rendered){
32475             this.renderIndent(true, true);
32476         }
32477     },
32478
32479     // private override
32480     setLastChild : function(node){
32481         var ol = this.lastChild;
32482         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
32483         if(this.childrenRendered && ol && node != ol){
32484             ol.renderIndent(true, true);
32485         }
32486         if(this.rendered){
32487             this.renderIndent(true, true);
32488         }
32489     },
32490
32491     // these methods are overridden to provide lazy rendering support
32492     // private override
32493     appendChild : function()
32494     {
32495         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
32496         if(node && this.childrenRendered){
32497             node.render();
32498         }
32499         this.ui.updateExpandIcon();
32500         return node;
32501     },
32502
32503     // private override
32504     removeChild : function(node){
32505         this.ownerTree.getSelectionModel().unselect(node);
32506         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
32507         // if it's been rendered remove dom node
32508         if(this.childrenRendered){
32509             node.ui.remove();
32510         }
32511         if(this.childNodes.length < 1){
32512             this.collapse(false, false);
32513         }else{
32514             this.ui.updateExpandIcon();
32515         }
32516         if(!this.firstChild) {
32517             this.childrenRendered = false;
32518         }
32519         return node;
32520     },
32521
32522     // private override
32523     insertBefore : function(node, refNode){
32524         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
32525         if(newNode && refNode && this.childrenRendered){
32526             node.render();
32527         }
32528         this.ui.updateExpandIcon();
32529         return newNode;
32530     },
32531
32532     /**
32533      * Sets the text for this node
32534      * @param {String} text
32535      */
32536     setText : function(text){
32537         var oldText = this.text;
32538         this.text = text;
32539         this.attributes.text = text;
32540         if(this.rendered){ // event without subscribing
32541             this.ui.onTextChange(this, text, oldText);
32542         }
32543         this.fireEvent("textchange", this, text, oldText);
32544     },
32545
32546     /**
32547      * Triggers selection of this node
32548      */
32549     select : function(){
32550         this.getOwnerTree().getSelectionModel().select(this);
32551     },
32552
32553     /**
32554      * Triggers deselection of this node
32555      */
32556     unselect : function(){
32557         this.getOwnerTree().getSelectionModel().unselect(this);
32558     },
32559
32560     /**
32561      * Returns true if this node is selected
32562      * @return {Boolean}
32563      */
32564     isSelected : function(){
32565         return this.getOwnerTree().getSelectionModel().isSelected(this);
32566     },
32567
32568     /**
32569      * Expand this node.
32570      * @param {Boolean} deep (optional) True to expand all children as well
32571      * @param {Boolean} anim (optional) false to cancel the default animation
32572      * @param {Function} callback (optional) A callback to be called when
32573      * expanding this node completes (does not wait for deep expand to complete).
32574      * Called with 1 parameter, this node.
32575      */
32576     expand : function(deep, anim, callback){
32577         if(!this.expanded){
32578             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
32579                 return;
32580             }
32581             if(!this.childrenRendered){
32582                 this.renderChildren();
32583             }
32584             this.expanded = true;
32585             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
32586                 this.ui.animExpand(function(){
32587                     this.fireEvent("expand", this);
32588                     if(typeof callback == "function"){
32589                         callback(this);
32590                     }
32591                     if(deep === true){
32592                         this.expandChildNodes(true);
32593                     }
32594                 }.createDelegate(this));
32595                 return;
32596             }else{
32597                 this.ui.expand();
32598                 this.fireEvent("expand", this);
32599                 if(typeof callback == "function"){
32600                     callback(this);
32601                 }
32602             }
32603         }else{
32604            if(typeof callback == "function"){
32605                callback(this);
32606            }
32607         }
32608         if(deep === true){
32609             this.expandChildNodes(true);
32610         }
32611     },
32612
32613     isHiddenRoot : function(){
32614         return this.isRoot && !this.getOwnerTree().rootVisible;
32615     },
32616
32617     /**
32618      * Collapse this node.
32619      * @param {Boolean} deep (optional) True to collapse all children as well
32620      * @param {Boolean} anim (optional) false to cancel the default animation
32621      */
32622     collapse : function(deep, anim){
32623         if(this.expanded && !this.isHiddenRoot()){
32624             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32625                 return;
32626             }
32627             this.expanded = false;
32628             if((this.getOwnerTree().animate && anim !== false) || anim){
32629                 this.ui.animCollapse(function(){
32630                     this.fireEvent("collapse", this);
32631                     if(deep === true){
32632                         this.collapseChildNodes(true);
32633                     }
32634                 }.createDelegate(this));
32635                 return;
32636             }else{
32637                 this.ui.collapse();
32638                 this.fireEvent("collapse", this);
32639             }
32640         }
32641         if(deep === true){
32642             var cs = this.childNodes;
32643             for(var i = 0, len = cs.length; i < len; i++) {
32644                 cs[i].collapse(true, false);
32645             }
32646         }
32647     },
32648
32649     // private
32650     delayedExpand : function(delay){
32651         if(!this.expandProcId){
32652             this.expandProcId = this.expand.defer(delay, this);
32653         }
32654     },
32655
32656     // private
32657     cancelExpand : function(){
32658         if(this.expandProcId){
32659             clearTimeout(this.expandProcId);
32660         }
32661         this.expandProcId = false;
32662     },
32663
32664     /**
32665      * Toggles expanded/collapsed state of the node
32666      */
32667     toggle : function(){
32668         if(this.expanded){
32669             this.collapse();
32670         }else{
32671             this.expand();
32672         }
32673     },
32674
32675     /**
32676      * Ensures all parent nodes are expanded
32677      */
32678     ensureVisible : function(callback){
32679         var tree = this.getOwnerTree();
32680         tree.expandPath(this.parentNode.getPath(), false, function(){
32681             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32682             Roo.callback(callback);
32683         }.createDelegate(this));
32684     },
32685
32686     /**
32687      * Expand all child nodes
32688      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32689      */
32690     expandChildNodes : function(deep){
32691         var cs = this.childNodes;
32692         for(var i = 0, len = cs.length; i < len; i++) {
32693                 cs[i].expand(deep);
32694         }
32695     },
32696
32697     /**
32698      * Collapse all child nodes
32699      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32700      */
32701     collapseChildNodes : function(deep){
32702         var cs = this.childNodes;
32703         for(var i = 0, len = cs.length; i < len; i++) {
32704                 cs[i].collapse(deep);
32705         }
32706     },
32707
32708     /**
32709      * Disables this node
32710      */
32711     disable : function(){
32712         this.disabled = true;
32713         this.unselect();
32714         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32715             this.ui.onDisableChange(this, true);
32716         }
32717         this.fireEvent("disabledchange", this, true);
32718     },
32719
32720     /**
32721      * Enables this node
32722      */
32723     enable : function(){
32724         this.disabled = false;
32725         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32726             this.ui.onDisableChange(this, false);
32727         }
32728         this.fireEvent("disabledchange", this, false);
32729     },
32730
32731     // private
32732     renderChildren : function(suppressEvent){
32733         if(suppressEvent !== false){
32734             this.fireEvent("beforechildrenrendered", this);
32735         }
32736         var cs = this.childNodes;
32737         for(var i = 0, len = cs.length; i < len; i++){
32738             cs[i].render(true);
32739         }
32740         this.childrenRendered = true;
32741     },
32742
32743     // private
32744     sort : function(fn, scope){
32745         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32746         if(this.childrenRendered){
32747             var cs = this.childNodes;
32748             for(var i = 0, len = cs.length; i < len; i++){
32749                 cs[i].render(true);
32750             }
32751         }
32752     },
32753
32754     // private
32755     render : function(bulkRender){
32756         this.ui.render(bulkRender);
32757         if(!this.rendered){
32758             this.rendered = true;
32759             if(this.expanded){
32760                 this.expanded = false;
32761                 this.expand(false, false);
32762             }
32763         }
32764     },
32765
32766     // private
32767     renderIndent : function(deep, refresh){
32768         if(refresh){
32769             this.ui.childIndent = null;
32770         }
32771         this.ui.renderIndent();
32772         if(deep === true && this.childrenRendered){
32773             var cs = this.childNodes;
32774             for(var i = 0, len = cs.length; i < len; i++){
32775                 cs[i].renderIndent(true, refresh);
32776             }
32777         }
32778     }
32779 });/*
32780  * Based on:
32781  * Ext JS Library 1.1.1
32782  * Copyright(c) 2006-2007, Ext JS, LLC.
32783  *
32784  * Originally Released Under LGPL - original licence link has changed is not relivant.
32785  *
32786  * Fork - LGPL
32787  * <script type="text/javascript">
32788  */
32789  
32790 /**
32791  * @class Roo.tree.AsyncTreeNode
32792  * @extends Roo.tree.TreeNode
32793  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32794  * @constructor
32795  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32796  */
32797  Roo.tree.AsyncTreeNode = function(config){
32798     this.loaded = false;
32799     this.loading = false;
32800     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32801     /**
32802     * @event beforeload
32803     * Fires before this node is loaded, return false to cancel
32804     * @param {Node} this This node
32805     */
32806     this.addEvents({'beforeload':true, 'load': true});
32807     /**
32808     * @event load
32809     * Fires when this node is loaded
32810     * @param {Node} this This node
32811     */
32812     /**
32813      * The loader used by this node (defaults to using the tree's defined loader)
32814      * @type TreeLoader
32815      * @property loader
32816      */
32817 };
32818 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32819     expand : function(deep, anim, callback){
32820         if(this.loading){ // if an async load is already running, waiting til it's done
32821             var timer;
32822             var f = function(){
32823                 if(!this.loading){ // done loading
32824                     clearInterval(timer);
32825                     this.expand(deep, anim, callback);
32826                 }
32827             }.createDelegate(this);
32828             timer = setInterval(f, 200);
32829             return;
32830         }
32831         if(!this.loaded){
32832             if(this.fireEvent("beforeload", this) === false){
32833                 return;
32834             }
32835             this.loading = true;
32836             this.ui.beforeLoad(this);
32837             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32838             if(loader){
32839                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32840                 return;
32841             }
32842         }
32843         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32844     },
32845     
32846     /**
32847      * Returns true if this node is currently loading
32848      * @return {Boolean}
32849      */
32850     isLoading : function(){
32851         return this.loading;  
32852     },
32853     
32854     loadComplete : function(deep, anim, callback){
32855         this.loading = false;
32856         this.loaded = true;
32857         this.ui.afterLoad(this);
32858         this.fireEvent("load", this);
32859         this.expand(deep, anim, callback);
32860     },
32861     
32862     /**
32863      * Returns true if this node has been loaded
32864      * @return {Boolean}
32865      */
32866     isLoaded : function(){
32867         return this.loaded;
32868     },
32869     
32870     hasChildNodes : function(){
32871         if(!this.isLeaf() && !this.loaded){
32872             return true;
32873         }else{
32874             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32875         }
32876     },
32877
32878     /**
32879      * Trigger a reload for this node
32880      * @param {Function} callback
32881      */
32882     reload : function(callback){
32883         this.collapse(false, false);
32884         while(this.firstChild){
32885             this.removeChild(this.firstChild);
32886         }
32887         this.childrenRendered = false;
32888         this.loaded = false;
32889         if(this.isHiddenRoot()){
32890             this.expanded = false;
32891         }
32892         this.expand(false, false, callback);
32893     }
32894 });/*
32895  * Based on:
32896  * Ext JS Library 1.1.1
32897  * Copyright(c) 2006-2007, Ext JS, LLC.
32898  *
32899  * Originally Released Under LGPL - original licence link has changed is not relivant.
32900  *
32901  * Fork - LGPL
32902  * <script type="text/javascript">
32903  */
32904  
32905 /**
32906  * @class Roo.tree.TreeNodeUI
32907  * @constructor
32908  * @param {Object} node The node to render
32909  * The TreeNode UI implementation is separate from the
32910  * tree implementation. Unless you are customizing the tree UI,
32911  * you should never have to use this directly.
32912  */
32913 Roo.tree.TreeNodeUI = function(node){
32914     this.node = node;
32915     this.rendered = false;
32916     this.animating = false;
32917     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32918 };
32919
32920 Roo.tree.TreeNodeUI.prototype = {
32921     removeChild : function(node){
32922         if(this.rendered){
32923             this.ctNode.removeChild(node.ui.getEl());
32924         }
32925     },
32926
32927     beforeLoad : function(){
32928          this.addClass("x-tree-node-loading");
32929     },
32930
32931     afterLoad : function(){
32932          this.removeClass("x-tree-node-loading");
32933     },
32934
32935     onTextChange : function(node, text, oldText){
32936         if(this.rendered){
32937             this.textNode.innerHTML = text;
32938         }
32939     },
32940
32941     onDisableChange : function(node, state){
32942         this.disabled = state;
32943         if(state){
32944             this.addClass("x-tree-node-disabled");
32945         }else{
32946             this.removeClass("x-tree-node-disabled");
32947         }
32948     },
32949
32950     onSelectedChange : function(state){
32951         if(state){
32952             this.focus();
32953             this.addClass("x-tree-selected");
32954         }else{
32955             //this.blur();
32956             this.removeClass("x-tree-selected");
32957         }
32958     },
32959
32960     onMove : function(tree, node, oldParent, newParent, index, refNode){
32961         this.childIndent = null;
32962         if(this.rendered){
32963             var targetNode = newParent.ui.getContainer();
32964             if(!targetNode){//target not rendered
32965                 this.holder = document.createElement("div");
32966                 this.holder.appendChild(this.wrap);
32967                 return;
32968             }
32969             var insertBefore = refNode ? refNode.ui.getEl() : null;
32970             if(insertBefore){
32971                 targetNode.insertBefore(this.wrap, insertBefore);
32972             }else{
32973                 targetNode.appendChild(this.wrap);
32974             }
32975             this.node.renderIndent(true);
32976         }
32977     },
32978
32979     addClass : function(cls){
32980         if(this.elNode){
32981             Roo.fly(this.elNode).addClass(cls);
32982         }
32983     },
32984
32985     removeClass : function(cls){
32986         if(this.elNode){
32987             Roo.fly(this.elNode).removeClass(cls);
32988         }
32989     },
32990
32991     remove : function(){
32992         if(this.rendered){
32993             this.holder = document.createElement("div");
32994             this.holder.appendChild(this.wrap);
32995         }
32996     },
32997
32998     fireEvent : function(){
32999         return this.node.fireEvent.apply(this.node, arguments);
33000     },
33001
33002     initEvents : function(){
33003         this.node.on("move", this.onMove, this);
33004         var E = Roo.EventManager;
33005         var a = this.anchor;
33006
33007         var el = Roo.fly(a, '_treeui');
33008
33009         if(Roo.isOpera){ // opera render bug ignores the CSS
33010             el.setStyle("text-decoration", "none");
33011         }
33012
33013         el.on("click", this.onClick, this);
33014         el.on("dblclick", this.onDblClick, this);
33015
33016         if(this.checkbox){
33017             Roo.EventManager.on(this.checkbox,
33018                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33019         }
33020
33021         el.on("contextmenu", this.onContextMenu, this);
33022
33023         var icon = Roo.fly(this.iconNode);
33024         icon.on("click", this.onClick, this);
33025         icon.on("dblclick", this.onDblClick, this);
33026         icon.on("contextmenu", this.onContextMenu, this);
33027         E.on(this.ecNode, "click", this.ecClick, this, true);
33028
33029         if(this.node.disabled){
33030             this.addClass("x-tree-node-disabled");
33031         }
33032         if(this.node.hidden){
33033             this.addClass("x-tree-node-disabled");
33034         }
33035         var ot = this.node.getOwnerTree();
33036         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33037         if(dd && (!this.node.isRoot || ot.rootVisible)){
33038             Roo.dd.Registry.register(this.elNode, {
33039                 node: this.node,
33040                 handles: this.getDDHandles(),
33041                 isHandle: false
33042             });
33043         }
33044     },
33045
33046     getDDHandles : function(){
33047         return [this.iconNode, this.textNode];
33048     },
33049
33050     hide : function(){
33051         if(this.rendered){
33052             this.wrap.style.display = "none";
33053         }
33054     },
33055
33056     show : function(){
33057         if(this.rendered){
33058             this.wrap.style.display = "";
33059         }
33060     },
33061
33062     onContextMenu : function(e){
33063         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33064             e.preventDefault();
33065             this.focus();
33066             this.fireEvent("contextmenu", this.node, e);
33067         }
33068     },
33069
33070     onClick : function(e){
33071         if(this.dropping){
33072             e.stopEvent();
33073             return;
33074         }
33075         if(this.fireEvent("beforeclick", this.node, e) !== false){
33076             if(!this.disabled && this.node.attributes.href){
33077                 this.fireEvent("click", this.node, e);
33078                 return;
33079             }
33080             e.preventDefault();
33081             if(this.disabled){
33082                 return;
33083             }
33084
33085             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33086                 this.node.toggle();
33087             }
33088
33089             this.fireEvent("click", this.node, e);
33090         }else{
33091             e.stopEvent();
33092         }
33093     },
33094
33095     onDblClick : function(e){
33096         e.preventDefault();
33097         if(this.disabled){
33098             return;
33099         }
33100         if(this.checkbox){
33101             this.toggleCheck();
33102         }
33103         if(!this.animating && this.node.hasChildNodes()){
33104             this.node.toggle();
33105         }
33106         this.fireEvent("dblclick", this.node, e);
33107     },
33108
33109     onCheckChange : function(){
33110         var checked = this.checkbox.checked;
33111         this.node.attributes.checked = checked;
33112         this.fireEvent('checkchange', this.node, checked);
33113     },
33114
33115     ecClick : function(e){
33116         if(!this.animating && this.node.hasChildNodes()){
33117             this.node.toggle();
33118         }
33119     },
33120
33121     startDrop : function(){
33122         this.dropping = true;
33123     },
33124
33125     // delayed drop so the click event doesn't get fired on a drop
33126     endDrop : function(){
33127        setTimeout(function(){
33128            this.dropping = false;
33129        }.createDelegate(this), 50);
33130     },
33131
33132     expand : function(){
33133         this.updateExpandIcon();
33134         this.ctNode.style.display = "";
33135     },
33136
33137     focus : function(){
33138         if(!this.node.preventHScroll){
33139             try{this.anchor.focus();
33140             }catch(e){}
33141         }else if(!Roo.isIE){
33142             try{
33143                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33144                 var l = noscroll.scrollLeft;
33145                 this.anchor.focus();
33146                 noscroll.scrollLeft = l;
33147             }catch(e){}
33148         }
33149     },
33150
33151     toggleCheck : function(value){
33152         var cb = this.checkbox;
33153         if(cb){
33154             cb.checked = (value === undefined ? !cb.checked : value);
33155         }
33156     },
33157
33158     blur : function(){
33159         try{
33160             this.anchor.blur();
33161         }catch(e){}
33162     },
33163
33164     animExpand : function(callback){
33165         var ct = Roo.get(this.ctNode);
33166         ct.stopFx();
33167         if(!this.node.hasChildNodes()){
33168             this.updateExpandIcon();
33169             this.ctNode.style.display = "";
33170             Roo.callback(callback);
33171             return;
33172         }
33173         this.animating = true;
33174         this.updateExpandIcon();
33175
33176         ct.slideIn('t', {
33177            callback : function(){
33178                this.animating = false;
33179                Roo.callback(callback);
33180             },
33181             scope: this,
33182             duration: this.node.ownerTree.duration || .25
33183         });
33184     },
33185
33186     highlight : function(){
33187         var tree = this.node.getOwnerTree();
33188         Roo.fly(this.wrap).highlight(
33189             tree.hlColor || "C3DAF9",
33190             {endColor: tree.hlBaseColor}
33191         );
33192     },
33193
33194     collapse : function(){
33195         this.updateExpandIcon();
33196         this.ctNode.style.display = "none";
33197     },
33198
33199     animCollapse : function(callback){
33200         var ct = Roo.get(this.ctNode);
33201         ct.enableDisplayMode('block');
33202         ct.stopFx();
33203
33204         this.animating = true;
33205         this.updateExpandIcon();
33206
33207         ct.slideOut('t', {
33208             callback : function(){
33209                this.animating = false;
33210                Roo.callback(callback);
33211             },
33212             scope: this,
33213             duration: this.node.ownerTree.duration || .25
33214         });
33215     },
33216
33217     getContainer : function(){
33218         return this.ctNode;
33219     },
33220
33221     getEl : function(){
33222         return this.wrap;
33223     },
33224
33225     appendDDGhost : function(ghostNode){
33226         ghostNode.appendChild(this.elNode.cloneNode(true));
33227     },
33228
33229     getDDRepairXY : function(){
33230         return Roo.lib.Dom.getXY(this.iconNode);
33231     },
33232
33233     onRender : function(){
33234         this.render();
33235     },
33236
33237     render : function(bulkRender){
33238         var n = this.node, a = n.attributes;
33239         var targetNode = n.parentNode ?
33240               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33241
33242         if(!this.rendered){
33243             this.rendered = true;
33244
33245             this.renderElements(n, a, targetNode, bulkRender);
33246
33247             if(a.qtip){
33248                if(this.textNode.setAttributeNS){
33249                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33250                    if(a.qtipTitle){
33251                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33252                    }
33253                }else{
33254                    this.textNode.setAttribute("ext:qtip", a.qtip);
33255                    if(a.qtipTitle){
33256                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33257                    }
33258                }
33259             }else if(a.qtipCfg){
33260                 a.qtipCfg.target = Roo.id(this.textNode);
33261                 Roo.QuickTips.register(a.qtipCfg);
33262             }
33263             this.initEvents();
33264             if(!this.node.expanded){
33265                 this.updateExpandIcon();
33266             }
33267         }else{
33268             if(bulkRender === true) {
33269                 targetNode.appendChild(this.wrap);
33270             }
33271         }
33272     },
33273
33274     renderElements : function(n, a, targetNode, bulkRender)
33275     {
33276         // add some indent caching, this helps performance when rendering a large tree
33277         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33278         var t = n.getOwnerTree();
33279         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33280         if (typeof(n.attributes.html) != 'undefined') {
33281             txt = n.attributes.html;
33282         }
33283         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33284         var cb = typeof a.checked == 'boolean';
33285         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33286         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33287             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33288             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33289             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33290             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33291             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33292              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33293                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33294             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33295             "</li>"];
33296
33297         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33298             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33299                                 n.nextSibling.ui.getEl(), buf.join(""));
33300         }else{
33301             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33302         }
33303
33304         this.elNode = this.wrap.childNodes[0];
33305         this.ctNode = this.wrap.childNodes[1];
33306         var cs = this.elNode.childNodes;
33307         this.indentNode = cs[0];
33308         this.ecNode = cs[1];
33309         this.iconNode = cs[2];
33310         var index = 3;
33311         if(cb){
33312             this.checkbox = cs[3];
33313             index++;
33314         }
33315         this.anchor = cs[index];
33316         this.textNode = cs[index].firstChild;
33317     },
33318
33319     getAnchor : function(){
33320         return this.anchor;
33321     },
33322
33323     getTextEl : function(){
33324         return this.textNode;
33325     },
33326
33327     getIconEl : function(){
33328         return this.iconNode;
33329     },
33330
33331     isChecked : function(){
33332         return this.checkbox ? this.checkbox.checked : false;
33333     },
33334
33335     updateExpandIcon : function(){
33336         if(this.rendered){
33337             var n = this.node, c1, c2;
33338             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
33339             var hasChild = n.hasChildNodes();
33340             if(hasChild){
33341                 if(n.expanded){
33342                     cls += "-minus";
33343                     c1 = "x-tree-node-collapsed";
33344                     c2 = "x-tree-node-expanded";
33345                 }else{
33346                     cls += "-plus";
33347                     c1 = "x-tree-node-expanded";
33348                     c2 = "x-tree-node-collapsed";
33349                 }
33350                 if(this.wasLeaf){
33351                     this.removeClass("x-tree-node-leaf");
33352                     this.wasLeaf = false;
33353                 }
33354                 if(this.c1 != c1 || this.c2 != c2){
33355                     Roo.fly(this.elNode).replaceClass(c1, c2);
33356                     this.c1 = c1; this.c2 = c2;
33357                 }
33358             }else{
33359                 // this changes non-leafs into leafs if they have no children.
33360                 // it's not very rational behaviour..
33361                 
33362                 if(!this.wasLeaf && this.node.leaf){
33363                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
33364                     delete this.c1;
33365                     delete this.c2;
33366                     this.wasLeaf = true;
33367                 }
33368             }
33369             var ecc = "x-tree-ec-icon "+cls;
33370             if(this.ecc != ecc){
33371                 this.ecNode.className = ecc;
33372                 this.ecc = ecc;
33373             }
33374         }
33375     },
33376
33377     getChildIndent : function(){
33378         if(!this.childIndent){
33379             var buf = [];
33380             var p = this.node;
33381             while(p){
33382                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
33383                     if(!p.isLast()) {
33384                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
33385                     } else {
33386                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
33387                     }
33388                 }
33389                 p = p.parentNode;
33390             }
33391             this.childIndent = buf.join("");
33392         }
33393         return this.childIndent;
33394     },
33395
33396     renderIndent : function(){
33397         if(this.rendered){
33398             var indent = "";
33399             var p = this.node.parentNode;
33400             if(p){
33401                 indent = p.ui.getChildIndent();
33402             }
33403             if(this.indentMarkup != indent){ // don't rerender if not required
33404                 this.indentNode.innerHTML = indent;
33405                 this.indentMarkup = indent;
33406             }
33407             this.updateExpandIcon();
33408         }
33409     }
33410 };
33411
33412 Roo.tree.RootTreeNodeUI = function(){
33413     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
33414 };
33415 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
33416     render : function(){
33417         if(!this.rendered){
33418             var targetNode = this.node.ownerTree.innerCt.dom;
33419             this.node.expanded = true;
33420             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
33421             this.wrap = this.ctNode = targetNode.firstChild;
33422         }
33423     },
33424     collapse : function(){
33425     },
33426     expand : function(){
33427     }
33428 });/*
33429  * Based on:
33430  * Ext JS Library 1.1.1
33431  * Copyright(c) 2006-2007, Ext JS, LLC.
33432  *
33433  * Originally Released Under LGPL - original licence link has changed is not relivant.
33434  *
33435  * Fork - LGPL
33436  * <script type="text/javascript">
33437  */
33438 /**
33439  * @class Roo.tree.TreeLoader
33440  * @extends Roo.util.Observable
33441  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
33442  * nodes from a specified URL. The response must be a javascript Array definition
33443  * who's elements are node definition objects. eg:
33444  * <pre><code>
33445 {  success : true,
33446    data :      [
33447    
33448     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
33449     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
33450     ]
33451 }
33452
33453
33454 </code></pre>
33455  * <br><br>
33456  * The old style respose with just an array is still supported, but not recommended.
33457  * <br><br>
33458  *
33459  * A server request is sent, and child nodes are loaded only when a node is expanded.
33460  * The loading node's id is passed to the server under the parameter name "node" to
33461  * enable the server to produce the correct child nodes.
33462  * <br><br>
33463  * To pass extra parameters, an event handler may be attached to the "beforeload"
33464  * event, and the parameters specified in the TreeLoader's baseParams property:
33465  * <pre><code>
33466     myTreeLoader.on("beforeload", function(treeLoader, node) {
33467         this.baseParams.category = node.attributes.category;
33468     }, this);
33469 </code></pre><
33470  * This would pass an HTTP parameter called "category" to the server containing
33471  * the value of the Node's "category" attribute.
33472  * @constructor
33473  * Creates a new Treeloader.
33474  * @param {Object} config A config object containing config properties.
33475  */
33476 Roo.tree.TreeLoader = function(config){
33477     this.baseParams = {};
33478     this.requestMethod = "POST";
33479     Roo.apply(this, config);
33480
33481     this.addEvents({
33482     
33483         /**
33484          * @event beforeload
33485          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
33486          * @param {Object} This TreeLoader object.
33487          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33488          * @param {Object} callback The callback function specified in the {@link #load} call.
33489          */
33490         beforeload : true,
33491         /**
33492          * @event load
33493          * Fires when the node has been successfuly loaded.
33494          * @param {Object} This TreeLoader object.
33495          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33496          * @param {Object} response The response object containing the data from the server.
33497          */
33498         load : true,
33499         /**
33500          * @event loadexception
33501          * Fires if the network request failed.
33502          * @param {Object} This TreeLoader object.
33503          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33504          * @param {Object} response The response object containing the data from the server.
33505          */
33506         loadexception : true,
33507         /**
33508          * @event create
33509          * Fires before a node is created, enabling you to return custom Node types 
33510          * @param {Object} This TreeLoader object.
33511          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
33512          */
33513         create : true
33514     });
33515
33516     Roo.tree.TreeLoader.superclass.constructor.call(this);
33517 };
33518
33519 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
33520     /**
33521     * @cfg {String} dataUrl The URL from which to request a Json string which
33522     * specifies an array of node definition object representing the child nodes
33523     * to be loaded.
33524     */
33525     /**
33526     * @cfg {String} requestMethod either GET or POST
33527     * defaults to POST (due to BC)
33528     * to be loaded.
33529     */
33530     /**
33531     * @cfg {Object} baseParams (optional) An object containing properties which
33532     * specify HTTP parameters to be passed to each request for child nodes.
33533     */
33534     /**
33535     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
33536     * created by this loader. If the attributes sent by the server have an attribute in this object,
33537     * they take priority.
33538     */
33539     /**
33540     * @cfg {Object} uiProviders (optional) An object containing properties which
33541     * 
33542     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
33543     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
33544     * <i>uiProvider</i> attribute of a returned child node is a string rather
33545     * than a reference to a TreeNodeUI implementation, this that string value
33546     * is used as a property name in the uiProviders object. You can define the provider named
33547     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
33548     */
33549     uiProviders : {},
33550
33551     /**
33552     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
33553     * child nodes before loading.
33554     */
33555     clearOnLoad : true,
33556
33557     /**
33558     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
33559     * property on loading, rather than expecting an array. (eg. more compatible to a standard
33560     * Grid query { data : [ .....] }
33561     */
33562     
33563     root : false,
33564      /**
33565     * @cfg {String} queryParam (optional) 
33566     * Name of the query as it will be passed on the querystring (defaults to 'node')
33567     * eg. the request will be ?node=[id]
33568     */
33569     
33570     
33571     queryParam: false,
33572     
33573     /**
33574      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
33575      * This is called automatically when a node is expanded, but may be used to reload
33576      * a node (or append new children if the {@link #clearOnLoad} option is false.)
33577      * @param {Roo.tree.TreeNode} node
33578      * @param {Function} callback
33579      */
33580     load : function(node, callback){
33581         if(this.clearOnLoad){
33582             while(node.firstChild){
33583                 node.removeChild(node.firstChild);
33584             }
33585         }
33586         if(node.attributes.children){ // preloaded json children
33587             var cs = node.attributes.children;
33588             for(var i = 0, len = cs.length; i < len; i++){
33589                 node.appendChild(this.createNode(cs[i]));
33590             }
33591             if(typeof callback == "function"){
33592                 callback();
33593             }
33594         }else if(this.dataUrl){
33595             this.requestData(node, callback);
33596         }
33597     },
33598
33599     getParams: function(node){
33600         var buf = [], bp = this.baseParams;
33601         for(var key in bp){
33602             if(typeof bp[key] != "function"){
33603                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33604             }
33605         }
33606         var n = this.queryParam === false ? 'node' : this.queryParam;
33607         buf.push(n + "=", encodeURIComponent(node.id));
33608         return buf.join("");
33609     },
33610
33611     requestData : function(node, callback){
33612         if(this.fireEvent("beforeload", this, node, callback) !== false){
33613             this.transId = Roo.Ajax.request({
33614                 method:this.requestMethod,
33615                 url: this.dataUrl||this.url,
33616                 success: this.handleResponse,
33617                 failure: this.handleFailure,
33618                 scope: this,
33619                 argument: {callback: callback, node: node},
33620                 params: this.getParams(node)
33621             });
33622         }else{
33623             // if the load is cancelled, make sure we notify
33624             // the node that we are done
33625             if(typeof callback == "function"){
33626                 callback();
33627             }
33628         }
33629     },
33630
33631     isLoading : function(){
33632         return this.transId ? true : false;
33633     },
33634
33635     abort : function(){
33636         if(this.isLoading()){
33637             Roo.Ajax.abort(this.transId);
33638         }
33639     },
33640
33641     // private
33642     createNode : function(attr)
33643     {
33644         // apply baseAttrs, nice idea Corey!
33645         if(this.baseAttrs){
33646             Roo.applyIf(attr, this.baseAttrs);
33647         }
33648         if(this.applyLoader !== false){
33649             attr.loader = this;
33650         }
33651         // uiProvider = depreciated..
33652         
33653         if(typeof(attr.uiProvider) == 'string'){
33654            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33655                 /**  eval:var:attr */ eval(attr.uiProvider);
33656         }
33657         if(typeof(this.uiProviders['default']) != 'undefined') {
33658             attr.uiProvider = this.uiProviders['default'];
33659         }
33660         
33661         this.fireEvent('create', this, attr);
33662         
33663         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33664         return(attr.leaf ?
33665                         new Roo.tree.TreeNode(attr) :
33666                         new Roo.tree.AsyncTreeNode(attr));
33667     },
33668
33669     processResponse : function(response, node, callback)
33670     {
33671         var json = response.responseText;
33672         try {
33673             
33674             var o = Roo.decode(json);
33675             
33676             if (this.root === false && typeof(o.success) != undefined) {
33677                 this.root = 'data'; // the default behaviour for list like data..
33678                 }
33679                 
33680             if (this.root !== false &&  !o.success) {
33681                 // it's a failure condition.
33682                 var a = response.argument;
33683                 this.fireEvent("loadexception", this, a.node, response);
33684                 Roo.log("Load failed - should have a handler really");
33685                 return;
33686             }
33687             
33688             
33689             
33690             if (this.root !== false) {
33691                  o = o[this.root];
33692             }
33693             
33694             for(var i = 0, len = o.length; i < len; i++){
33695                 var n = this.createNode(o[i]);
33696                 if(n){
33697                     node.appendChild(n);
33698                 }
33699             }
33700             if(typeof callback == "function"){
33701                 callback(this, node);
33702             }
33703         }catch(e){
33704             this.handleFailure(response);
33705         }
33706     },
33707
33708     handleResponse : function(response){
33709         this.transId = false;
33710         var a = response.argument;
33711         this.processResponse(response, a.node, a.callback);
33712         this.fireEvent("load", this, a.node, response);
33713     },
33714
33715     handleFailure : function(response)
33716     {
33717         // should handle failure better..
33718         this.transId = false;
33719         var a = response.argument;
33720         this.fireEvent("loadexception", this, a.node, response);
33721         if(typeof a.callback == "function"){
33722             a.callback(this, a.node);
33723         }
33724     }
33725 });/*
33726  * Based on:
33727  * Ext JS Library 1.1.1
33728  * Copyright(c) 2006-2007, Ext JS, LLC.
33729  *
33730  * Originally Released Under LGPL - original licence link has changed is not relivant.
33731  *
33732  * Fork - LGPL
33733  * <script type="text/javascript">
33734  */
33735
33736 /**
33737 * @class Roo.tree.TreeFilter
33738 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33739 * @param {TreePanel} tree
33740 * @param {Object} config (optional)
33741  */
33742 Roo.tree.TreeFilter = function(tree, config){
33743     this.tree = tree;
33744     this.filtered = {};
33745     Roo.apply(this, config);
33746 };
33747
33748 Roo.tree.TreeFilter.prototype = {
33749     clearBlank:false,
33750     reverse:false,
33751     autoClear:false,
33752     remove:false,
33753
33754      /**
33755      * Filter the data by a specific attribute.
33756      * @param {String/RegExp} value Either string that the attribute value
33757      * should start with or a RegExp to test against the attribute
33758      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33759      * @param {TreeNode} startNode (optional) The node to start the filter at.
33760      */
33761     filter : function(value, attr, startNode){
33762         attr = attr || "text";
33763         var f;
33764         if(typeof value == "string"){
33765             var vlen = value.length;
33766             // auto clear empty filter
33767             if(vlen == 0 && this.clearBlank){
33768                 this.clear();
33769                 return;
33770             }
33771             value = value.toLowerCase();
33772             f = function(n){
33773                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33774             };
33775         }else if(value.exec){ // regex?
33776             f = function(n){
33777                 return value.test(n.attributes[attr]);
33778             };
33779         }else{
33780             throw 'Illegal filter type, must be string or regex';
33781         }
33782         this.filterBy(f, null, startNode);
33783         },
33784
33785     /**
33786      * Filter by a function. The passed function will be called with each
33787      * node in the tree (or from the startNode). If the function returns true, the node is kept
33788      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33789      * @param {Function} fn The filter function
33790      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33791      */
33792     filterBy : function(fn, scope, startNode){
33793         startNode = startNode || this.tree.root;
33794         if(this.autoClear){
33795             this.clear();
33796         }
33797         var af = this.filtered, rv = this.reverse;
33798         var f = function(n){
33799             if(n == startNode){
33800                 return true;
33801             }
33802             if(af[n.id]){
33803                 return false;
33804             }
33805             var m = fn.call(scope || n, n);
33806             if(!m || rv){
33807                 af[n.id] = n;
33808                 n.ui.hide();
33809                 return false;
33810             }
33811             return true;
33812         };
33813         startNode.cascade(f);
33814         if(this.remove){
33815            for(var id in af){
33816                if(typeof id != "function"){
33817                    var n = af[id];
33818                    if(n && n.parentNode){
33819                        n.parentNode.removeChild(n);
33820                    }
33821                }
33822            }
33823         }
33824     },
33825
33826     /**
33827      * Clears the current filter. Note: with the "remove" option
33828      * set a filter cannot be cleared.
33829      */
33830     clear : function(){
33831         var t = this.tree;
33832         var af = this.filtered;
33833         for(var id in af){
33834             if(typeof id != "function"){
33835                 var n = af[id];
33836                 if(n){
33837                     n.ui.show();
33838                 }
33839             }
33840         }
33841         this.filtered = {};
33842     }
33843 };
33844 /*
33845  * Based on:
33846  * Ext JS Library 1.1.1
33847  * Copyright(c) 2006-2007, Ext JS, LLC.
33848  *
33849  * Originally Released Under LGPL - original licence link has changed is not relivant.
33850  *
33851  * Fork - LGPL
33852  * <script type="text/javascript">
33853  */
33854  
33855
33856 /**
33857  * @class Roo.tree.TreeSorter
33858  * Provides sorting of nodes in a TreePanel
33859  * 
33860  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33861  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33862  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33863  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33864  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33865  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33866  * @constructor
33867  * @param {TreePanel} tree
33868  * @param {Object} config
33869  */
33870 Roo.tree.TreeSorter = function(tree, config){
33871     Roo.apply(this, config);
33872     tree.on("beforechildrenrendered", this.doSort, this);
33873     tree.on("append", this.updateSort, this);
33874     tree.on("insert", this.updateSort, this);
33875     
33876     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33877     var p = this.property || "text";
33878     var sortType = this.sortType;
33879     var fs = this.folderSort;
33880     var cs = this.caseSensitive === true;
33881     var leafAttr = this.leafAttr || 'leaf';
33882
33883     this.sortFn = function(n1, n2){
33884         if(fs){
33885             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33886                 return 1;
33887             }
33888             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33889                 return -1;
33890             }
33891         }
33892         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33893         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33894         if(v1 < v2){
33895                         return dsc ? +1 : -1;
33896                 }else if(v1 > v2){
33897                         return dsc ? -1 : +1;
33898         }else{
33899                 return 0;
33900         }
33901     };
33902 };
33903
33904 Roo.tree.TreeSorter.prototype = {
33905     doSort : function(node){
33906         node.sort(this.sortFn);
33907     },
33908     
33909     compareNodes : function(n1, n2){
33910         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33911     },
33912     
33913     updateSort : function(tree, node){
33914         if(node.childrenRendered){
33915             this.doSort.defer(1, this, [node]);
33916         }
33917     }
33918 };/*
33919  * Based on:
33920  * Ext JS Library 1.1.1
33921  * Copyright(c) 2006-2007, Ext JS, LLC.
33922  *
33923  * Originally Released Under LGPL - original licence link has changed is not relivant.
33924  *
33925  * Fork - LGPL
33926  * <script type="text/javascript">
33927  */
33928
33929 if(Roo.dd.DropZone){
33930     
33931 Roo.tree.TreeDropZone = function(tree, config){
33932     this.allowParentInsert = false;
33933     this.allowContainerDrop = false;
33934     this.appendOnly = false;
33935     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33936     this.tree = tree;
33937     this.lastInsertClass = "x-tree-no-status";
33938     this.dragOverData = {};
33939 };
33940
33941 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33942     ddGroup : "TreeDD",
33943     scroll:  true,
33944     
33945     expandDelay : 1000,
33946     
33947     expandNode : function(node){
33948         if(node.hasChildNodes() && !node.isExpanded()){
33949             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33950         }
33951     },
33952     
33953     queueExpand : function(node){
33954         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33955     },
33956     
33957     cancelExpand : function(){
33958         if(this.expandProcId){
33959             clearTimeout(this.expandProcId);
33960             this.expandProcId = false;
33961         }
33962     },
33963     
33964     isValidDropPoint : function(n, pt, dd, e, data){
33965         if(!n || !data){ return false; }
33966         var targetNode = n.node;
33967         var dropNode = data.node;
33968         // default drop rules
33969         if(!(targetNode && targetNode.isTarget && pt)){
33970             return false;
33971         }
33972         if(pt == "append" && targetNode.allowChildren === false){
33973             return false;
33974         }
33975         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33976             return false;
33977         }
33978         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33979             return false;
33980         }
33981         // reuse the object
33982         var overEvent = this.dragOverData;
33983         overEvent.tree = this.tree;
33984         overEvent.target = targetNode;
33985         overEvent.data = data;
33986         overEvent.point = pt;
33987         overEvent.source = dd;
33988         overEvent.rawEvent = e;
33989         overEvent.dropNode = dropNode;
33990         overEvent.cancel = false;  
33991         var result = this.tree.fireEvent("nodedragover", overEvent);
33992         return overEvent.cancel === false && result !== false;
33993     },
33994     
33995     getDropPoint : function(e, n, dd)
33996     {
33997         var tn = n.node;
33998         if(tn.isRoot){
33999             return tn.allowChildren !== false ? "append" : false; // always append for root
34000         }
34001         var dragEl = n.ddel;
34002         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34003         var y = Roo.lib.Event.getPageY(e);
34004         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34005         
34006         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34007         var noAppend = tn.allowChildren === false;
34008         if(this.appendOnly || tn.parentNode.allowChildren === false){
34009             return noAppend ? false : "append";
34010         }
34011         var noBelow = false;
34012         if(!this.allowParentInsert){
34013             noBelow = tn.hasChildNodes() && tn.isExpanded();
34014         }
34015         var q = (b - t) / (noAppend ? 2 : 3);
34016         if(y >= t && y < (t + q)){
34017             return "above";
34018         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34019             return "below";
34020         }else{
34021             return "append";
34022         }
34023     },
34024     
34025     onNodeEnter : function(n, dd, e, data)
34026     {
34027         this.cancelExpand();
34028     },
34029     
34030     onNodeOver : function(n, dd, e, data)
34031     {
34032        
34033         var pt = this.getDropPoint(e, n, dd);
34034         var node = n.node;
34035         
34036         // auto node expand check
34037         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34038             this.queueExpand(node);
34039         }else if(pt != "append"){
34040             this.cancelExpand();
34041         }
34042         
34043         // set the insert point style on the target node
34044         var returnCls = this.dropNotAllowed;
34045         if(this.isValidDropPoint(n, pt, dd, e, data)){
34046            if(pt){
34047                var el = n.ddel;
34048                var cls;
34049                if(pt == "above"){
34050                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34051                    cls = "x-tree-drag-insert-above";
34052                }else if(pt == "below"){
34053                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34054                    cls = "x-tree-drag-insert-below";
34055                }else{
34056                    returnCls = "x-tree-drop-ok-append";
34057                    cls = "x-tree-drag-append";
34058                }
34059                if(this.lastInsertClass != cls){
34060                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34061                    this.lastInsertClass = cls;
34062                }
34063            }
34064        }
34065        return returnCls;
34066     },
34067     
34068     onNodeOut : function(n, dd, e, data){
34069         
34070         this.cancelExpand();
34071         this.removeDropIndicators(n);
34072     },
34073     
34074     onNodeDrop : function(n, dd, e, data){
34075         var point = this.getDropPoint(e, n, dd);
34076         var targetNode = n.node;
34077         targetNode.ui.startDrop();
34078         if(!this.isValidDropPoint(n, point, dd, e, data)){
34079             targetNode.ui.endDrop();
34080             return false;
34081         }
34082         // first try to find the drop node
34083         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34084         var dropEvent = {
34085             tree : this.tree,
34086             target: targetNode,
34087             data: data,
34088             point: point,
34089             source: dd,
34090             rawEvent: e,
34091             dropNode: dropNode,
34092             cancel: !dropNode   
34093         };
34094         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34095         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34096             targetNode.ui.endDrop();
34097             return false;
34098         }
34099         // allow target changing
34100         targetNode = dropEvent.target;
34101         if(point == "append" && !targetNode.isExpanded()){
34102             targetNode.expand(false, null, function(){
34103                 this.completeDrop(dropEvent);
34104             }.createDelegate(this));
34105         }else{
34106             this.completeDrop(dropEvent);
34107         }
34108         return true;
34109     },
34110     
34111     completeDrop : function(de){
34112         var ns = de.dropNode, p = de.point, t = de.target;
34113         if(!(ns instanceof Array)){
34114             ns = [ns];
34115         }
34116         var n;
34117         for(var i = 0, len = ns.length; i < len; i++){
34118             n = ns[i];
34119             if(p == "above"){
34120                 t.parentNode.insertBefore(n, t);
34121             }else if(p == "below"){
34122                 t.parentNode.insertBefore(n, t.nextSibling);
34123             }else{
34124                 t.appendChild(n);
34125             }
34126         }
34127         n.ui.focus();
34128         if(this.tree.hlDrop){
34129             n.ui.highlight();
34130         }
34131         t.ui.endDrop();
34132         this.tree.fireEvent("nodedrop", de);
34133     },
34134     
34135     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34136         if(this.tree.hlDrop){
34137             dropNode.ui.focus();
34138             dropNode.ui.highlight();
34139         }
34140         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34141     },
34142     
34143     getTree : function(){
34144         return this.tree;
34145     },
34146     
34147     removeDropIndicators : function(n){
34148         if(n && n.ddel){
34149             var el = n.ddel;
34150             Roo.fly(el).removeClass([
34151                     "x-tree-drag-insert-above",
34152                     "x-tree-drag-insert-below",
34153                     "x-tree-drag-append"]);
34154             this.lastInsertClass = "_noclass";
34155         }
34156     },
34157     
34158     beforeDragDrop : function(target, e, id){
34159         this.cancelExpand();
34160         return true;
34161     },
34162     
34163     afterRepair : function(data){
34164         if(data && Roo.enableFx){
34165             data.node.ui.highlight();
34166         }
34167         this.hideProxy();
34168     } 
34169     
34170 });
34171
34172 }
34173 /*
34174  * Based on:
34175  * Ext JS Library 1.1.1
34176  * Copyright(c) 2006-2007, Ext JS, LLC.
34177  *
34178  * Originally Released Under LGPL - original licence link has changed is not relivant.
34179  *
34180  * Fork - LGPL
34181  * <script type="text/javascript">
34182  */
34183  
34184
34185 if(Roo.dd.DragZone){
34186 Roo.tree.TreeDragZone = function(tree, config){
34187     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34188     this.tree = tree;
34189 };
34190
34191 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34192     ddGroup : "TreeDD",
34193    
34194     onBeforeDrag : function(data, e){
34195         var n = data.node;
34196         return n && n.draggable && !n.disabled;
34197     },
34198      
34199     
34200     onInitDrag : function(e){
34201         var data = this.dragData;
34202         this.tree.getSelectionModel().select(data.node);
34203         this.proxy.update("");
34204         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34205         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34206     },
34207     
34208     getRepairXY : function(e, data){
34209         return data.node.ui.getDDRepairXY();
34210     },
34211     
34212     onEndDrag : function(data, e){
34213         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34214         
34215         
34216     },
34217     
34218     onValidDrop : function(dd, e, id){
34219         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34220         this.hideProxy();
34221     },
34222     
34223     beforeInvalidDrop : function(e, id){
34224         // this scrolls the original position back into view
34225         var sm = this.tree.getSelectionModel();
34226         sm.clearSelections();
34227         sm.select(this.dragData.node);
34228     }
34229 });
34230 }/*
34231  * Based on:
34232  * Ext JS Library 1.1.1
34233  * Copyright(c) 2006-2007, Ext JS, LLC.
34234  *
34235  * Originally Released Under LGPL - original licence link has changed is not relivant.
34236  *
34237  * Fork - LGPL
34238  * <script type="text/javascript">
34239  */
34240 /**
34241  * @class Roo.tree.TreeEditor
34242  * @extends Roo.Editor
34243  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34244  * as the editor field.
34245  * @constructor
34246  * @param {Object} config (used to be the tree panel.)
34247  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34248  * 
34249  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34250  * @cfg {Roo.form.TextField|Object} field The field configuration
34251  *
34252  * 
34253  */
34254 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34255     var tree = config;
34256     var field;
34257     if (oldconfig) { // old style..
34258         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34259     } else {
34260         // new style..
34261         tree = config.tree;
34262         config.field = config.field  || {};
34263         config.field.xtype = 'TextField';
34264         field = Roo.factory(config.field, Roo.form);
34265     }
34266     config = config || {};
34267     
34268     
34269     this.addEvents({
34270         /**
34271          * @event beforenodeedit
34272          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34273          * false from the handler of this event.
34274          * @param {Editor} this
34275          * @param {Roo.tree.Node} node 
34276          */
34277         "beforenodeedit" : true
34278     });
34279     
34280     //Roo.log(config);
34281     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34282
34283     this.tree = tree;
34284
34285     tree.on('beforeclick', this.beforeNodeClick, this);
34286     tree.getTreeEl().on('mousedown', this.hide, this);
34287     this.on('complete', this.updateNode, this);
34288     this.on('beforestartedit', this.fitToTree, this);
34289     this.on('startedit', this.bindScroll, this, {delay:10});
34290     this.on('specialkey', this.onSpecialKey, this);
34291 };
34292
34293 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34294     /**
34295      * @cfg {String} alignment
34296      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34297      */
34298     alignment: "l-l",
34299     // inherit
34300     autoSize: false,
34301     /**
34302      * @cfg {Boolean} hideEl
34303      * True to hide the bound element while the editor is displayed (defaults to false)
34304      */
34305     hideEl : false,
34306     /**
34307      * @cfg {String} cls
34308      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34309      */
34310     cls: "x-small-editor x-tree-editor",
34311     /**
34312      * @cfg {Boolean} shim
34313      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34314      */
34315     shim:false,
34316     // inherit
34317     shadow:"frame",
34318     /**
34319      * @cfg {Number} maxWidth
34320      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34321      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34322      * scroll and client offsets into account prior to each edit.
34323      */
34324     maxWidth: 250,
34325
34326     editDelay : 350,
34327
34328     // private
34329     fitToTree : function(ed, el){
34330         var td = this.tree.getTreeEl().dom, nd = el.dom;
34331         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34332             td.scrollLeft = nd.offsetLeft;
34333         }
34334         var w = Math.min(
34335                 this.maxWidth,
34336                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
34337         this.setSize(w, '');
34338         
34339         return this.fireEvent('beforenodeedit', this, this.editNode);
34340         
34341     },
34342
34343     // private
34344     triggerEdit : function(node){
34345         this.completeEdit();
34346         this.editNode = node;
34347         this.startEdit(node.ui.textNode, node.text);
34348     },
34349
34350     // private
34351     bindScroll : function(){
34352         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
34353     },
34354
34355     // private
34356     beforeNodeClick : function(node, e){
34357         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
34358         this.lastClick = new Date();
34359         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
34360             e.stopEvent();
34361             this.triggerEdit(node);
34362             return false;
34363         }
34364         return true;
34365     },
34366
34367     // private
34368     updateNode : function(ed, value){
34369         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
34370         this.editNode.setText(value);
34371     },
34372
34373     // private
34374     onHide : function(){
34375         Roo.tree.TreeEditor.superclass.onHide.call(this);
34376         if(this.editNode){
34377             this.editNode.ui.focus();
34378         }
34379     },
34380
34381     // private
34382     onSpecialKey : function(field, e){
34383         var k = e.getKey();
34384         if(k == e.ESC){
34385             e.stopEvent();
34386             this.cancelEdit();
34387         }else if(k == e.ENTER && !e.hasModifier()){
34388             e.stopEvent();
34389             this.completeEdit();
34390         }
34391     }
34392 });//<Script type="text/javascript">
34393 /*
34394  * Based on:
34395  * Ext JS Library 1.1.1
34396  * Copyright(c) 2006-2007, Ext JS, LLC.
34397  *
34398  * Originally Released Under LGPL - original licence link has changed is not relivant.
34399  *
34400  * Fork - LGPL
34401  * <script type="text/javascript">
34402  */
34403  
34404 /**
34405  * Not documented??? - probably should be...
34406  */
34407
34408 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
34409     //focus: Roo.emptyFn, // prevent odd scrolling behavior
34410     
34411     renderElements : function(n, a, targetNode, bulkRender){
34412         //consel.log("renderElements?");
34413         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34414
34415         var t = n.getOwnerTree();
34416         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
34417         
34418         var cols = t.columns;
34419         var bw = t.borderWidth;
34420         var c = cols[0];
34421         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34422          var cb = typeof a.checked == "boolean";
34423         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34424         var colcls = 'x-t-' + tid + '-c0';
34425         var buf = [
34426             '<li class="x-tree-node">',
34427             
34428                 
34429                 '<div class="x-tree-node-el ', a.cls,'">',
34430                     // extran...
34431                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
34432                 
34433                 
34434                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
34435                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
34436                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
34437                            (a.icon ? ' x-tree-node-inline-icon' : ''),
34438                            (a.iconCls ? ' '+a.iconCls : ''),
34439                            '" unselectable="on" />',
34440                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
34441                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
34442                              
34443                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34444                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
34445                             '<span unselectable="on" qtip="' + tx + '">',
34446                              tx,
34447                              '</span></a>' ,
34448                     '</div>',
34449                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34450                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
34451                  ];
34452         for(var i = 1, len = cols.length; i < len; i++){
34453             c = cols[i];
34454             colcls = 'x-t-' + tid + '-c' +i;
34455             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34456             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
34457                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
34458                       "</div>");
34459          }
34460          
34461          buf.push(
34462             '</a>',
34463             '<div class="x-clear"></div></div>',
34464             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34465             "</li>");
34466         
34467         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34468             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34469                                 n.nextSibling.ui.getEl(), buf.join(""));
34470         }else{
34471             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34472         }
34473         var el = this.wrap.firstChild;
34474         this.elRow = el;
34475         this.elNode = el.firstChild;
34476         this.ranchor = el.childNodes[1];
34477         this.ctNode = this.wrap.childNodes[1];
34478         var cs = el.firstChild.childNodes;
34479         this.indentNode = cs[0];
34480         this.ecNode = cs[1];
34481         this.iconNode = cs[2];
34482         var index = 3;
34483         if(cb){
34484             this.checkbox = cs[3];
34485             index++;
34486         }
34487         this.anchor = cs[index];
34488         
34489         this.textNode = cs[index].firstChild;
34490         
34491         //el.on("click", this.onClick, this);
34492         //el.on("dblclick", this.onDblClick, this);
34493         
34494         
34495        // console.log(this);
34496     },
34497     initEvents : function(){
34498         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
34499         
34500             
34501         var a = this.ranchor;
34502
34503         var el = Roo.get(a);
34504
34505         if(Roo.isOpera){ // opera render bug ignores the CSS
34506             el.setStyle("text-decoration", "none");
34507         }
34508
34509         el.on("click", this.onClick, this);
34510         el.on("dblclick", this.onDblClick, this);
34511         el.on("contextmenu", this.onContextMenu, this);
34512         
34513     },
34514     
34515     /*onSelectedChange : function(state){
34516         if(state){
34517             this.focus();
34518             this.addClass("x-tree-selected");
34519         }else{
34520             //this.blur();
34521             this.removeClass("x-tree-selected");
34522         }
34523     },*/
34524     addClass : function(cls){
34525         if(this.elRow){
34526             Roo.fly(this.elRow).addClass(cls);
34527         }
34528         
34529     },
34530     
34531     
34532     removeClass : function(cls){
34533         if(this.elRow){
34534             Roo.fly(this.elRow).removeClass(cls);
34535         }
34536     }
34537
34538     
34539     
34540 });//<Script type="text/javascript">
34541
34542 /*
34543  * Based on:
34544  * Ext JS Library 1.1.1
34545  * Copyright(c) 2006-2007, Ext JS, LLC.
34546  *
34547  * Originally Released Under LGPL - original licence link has changed is not relivant.
34548  *
34549  * Fork - LGPL
34550  * <script type="text/javascript">
34551  */
34552  
34553
34554 /**
34555  * @class Roo.tree.ColumnTree
34556  * @extends Roo.data.TreePanel
34557  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
34558  * @cfg {int} borderWidth  compined right/left border allowance
34559  * @constructor
34560  * @param {String/HTMLElement/Element} el The container element
34561  * @param {Object} config
34562  */
34563 Roo.tree.ColumnTree =  function(el, config)
34564 {
34565    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
34566    this.addEvents({
34567         /**
34568         * @event resize
34569         * Fire this event on a container when it resizes
34570         * @param {int} w Width
34571         * @param {int} h Height
34572         */
34573        "resize" : true
34574     });
34575     this.on('resize', this.onResize, this);
34576 };
34577
34578 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
34579     //lines:false,
34580     
34581     
34582     borderWidth: Roo.isBorderBox ? 0 : 2, 
34583     headEls : false,
34584     
34585     render : function(){
34586         // add the header.....
34587        
34588         Roo.tree.ColumnTree.superclass.render.apply(this);
34589         
34590         this.el.addClass('x-column-tree');
34591         
34592         this.headers = this.el.createChild(
34593             {cls:'x-tree-headers'},this.innerCt.dom);
34594    
34595         var cols = this.columns, c;
34596         var totalWidth = 0;
34597         this.headEls = [];
34598         var  len = cols.length;
34599         for(var i = 0; i < len; i++){
34600              c = cols[i];
34601              totalWidth += c.width;
34602             this.headEls.push(this.headers.createChild({
34603                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
34604                  cn: {
34605                      cls:'x-tree-hd-text',
34606                      html: c.header
34607                  },
34608                  style:'width:'+(c.width-this.borderWidth)+'px;'
34609              }));
34610         }
34611         this.headers.createChild({cls:'x-clear'});
34612         // prevent floats from wrapping when clipped
34613         this.headers.setWidth(totalWidth);
34614         //this.innerCt.setWidth(totalWidth);
34615         this.innerCt.setStyle({ overflow: 'auto' });
34616         this.onResize(this.width, this.height);
34617              
34618         
34619     },
34620     onResize : function(w,h)
34621     {
34622         this.height = h;
34623         this.width = w;
34624         // resize cols..
34625         this.innerCt.setWidth(this.width);
34626         this.innerCt.setHeight(this.height-20);
34627         
34628         // headers...
34629         var cols = this.columns, c;
34630         var totalWidth = 0;
34631         var expEl = false;
34632         var len = cols.length;
34633         for(var i = 0; i < len; i++){
34634             c = cols[i];
34635             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34636                 // it's the expander..
34637                 expEl  = this.headEls[i];
34638                 continue;
34639             }
34640             totalWidth += c.width;
34641             
34642         }
34643         if (expEl) {
34644             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34645         }
34646         this.headers.setWidth(w-20);
34647
34648         
34649         
34650         
34651     }
34652 });
34653 /*
34654  * Based on:
34655  * Ext JS Library 1.1.1
34656  * Copyright(c) 2006-2007, Ext JS, LLC.
34657  *
34658  * Originally Released Under LGPL - original licence link has changed is not relivant.
34659  *
34660  * Fork - LGPL
34661  * <script type="text/javascript">
34662  */
34663  
34664 /**
34665  * @class Roo.menu.Menu
34666  * @extends Roo.util.Observable
34667  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34668  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34669  * @constructor
34670  * Creates a new Menu
34671  * @param {Object} config Configuration options
34672  */
34673 Roo.menu.Menu = function(config){
34674     Roo.apply(this, config);
34675     this.id = this.id || Roo.id();
34676     this.addEvents({
34677         /**
34678          * @event beforeshow
34679          * Fires before this menu is displayed
34680          * @param {Roo.menu.Menu} this
34681          */
34682         beforeshow : true,
34683         /**
34684          * @event beforehide
34685          * Fires before this menu is hidden
34686          * @param {Roo.menu.Menu} this
34687          */
34688         beforehide : true,
34689         /**
34690          * @event show
34691          * Fires after this menu is displayed
34692          * @param {Roo.menu.Menu} this
34693          */
34694         show : true,
34695         /**
34696          * @event hide
34697          * Fires after this menu is hidden
34698          * @param {Roo.menu.Menu} this
34699          */
34700         hide : true,
34701         /**
34702          * @event click
34703          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34704          * @param {Roo.menu.Menu} this
34705          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34706          * @param {Roo.EventObject} e
34707          */
34708         click : true,
34709         /**
34710          * @event mouseover
34711          * Fires when the mouse is hovering over this menu
34712          * @param {Roo.menu.Menu} this
34713          * @param {Roo.EventObject} e
34714          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34715          */
34716         mouseover : true,
34717         /**
34718          * @event mouseout
34719          * Fires when the mouse exits this menu
34720          * @param {Roo.menu.Menu} this
34721          * @param {Roo.EventObject} e
34722          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34723          */
34724         mouseout : true,
34725         /**
34726          * @event itemclick
34727          * Fires when a menu item contained in this menu is clicked
34728          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34729          * @param {Roo.EventObject} e
34730          */
34731         itemclick: true
34732     });
34733     if (this.registerMenu) {
34734         Roo.menu.MenuMgr.register(this);
34735     }
34736     
34737     var mis = this.items;
34738     this.items = new Roo.util.MixedCollection();
34739     if(mis){
34740         this.add.apply(this, mis);
34741     }
34742 };
34743
34744 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34745     /**
34746      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34747      */
34748     minWidth : 120,
34749     /**
34750      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34751      * for bottom-right shadow (defaults to "sides")
34752      */
34753     shadow : "sides",
34754     /**
34755      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34756      * this menu (defaults to "tl-tr?")
34757      */
34758     subMenuAlign : "tl-tr?",
34759     /**
34760      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34761      * relative to its element of origin (defaults to "tl-bl?")
34762      */
34763     defaultAlign : "tl-bl?",
34764     /**
34765      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34766      */
34767     allowOtherMenus : false,
34768     /**
34769      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34770      */
34771     registerMenu : true,
34772
34773     hidden:true,
34774
34775     // private
34776     render : function(){
34777         if(this.el){
34778             return;
34779         }
34780         var el = this.el = new Roo.Layer({
34781             cls: "x-menu",
34782             shadow:this.shadow,
34783             constrain: false,
34784             parentEl: this.parentEl || document.body,
34785             zindex:15000
34786         });
34787
34788         this.keyNav = new Roo.menu.MenuNav(this);
34789
34790         if(this.plain){
34791             el.addClass("x-menu-plain");
34792         }
34793         if(this.cls){
34794             el.addClass(this.cls);
34795         }
34796         // generic focus element
34797         this.focusEl = el.createChild({
34798             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34799         });
34800         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34801         ul.on("click", this.onClick, this);
34802         ul.on("mouseover", this.onMouseOver, this);
34803         ul.on("mouseout", this.onMouseOut, this);
34804         this.items.each(function(item){
34805             var li = document.createElement("li");
34806             li.className = "x-menu-list-item";
34807             ul.dom.appendChild(li);
34808             item.render(li, this);
34809         }, this);
34810         this.ul = ul;
34811         this.autoWidth();
34812     },
34813
34814     // private
34815     autoWidth : function(){
34816         var el = this.el, ul = this.ul;
34817         if(!el){
34818             return;
34819         }
34820         var w = this.width;
34821         if(w){
34822             el.setWidth(w);
34823         }else if(Roo.isIE){
34824             el.setWidth(this.minWidth);
34825             var t = el.dom.offsetWidth; // force recalc
34826             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34827         }
34828     },
34829
34830     // private
34831     delayAutoWidth : function(){
34832         if(this.rendered){
34833             if(!this.awTask){
34834                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34835             }
34836             this.awTask.delay(20);
34837         }
34838     },
34839
34840     // private
34841     findTargetItem : function(e){
34842         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34843         if(t && t.menuItemId){
34844             return this.items.get(t.menuItemId);
34845         }
34846     },
34847
34848     // private
34849     onClick : function(e){
34850         var t;
34851         if(t = this.findTargetItem(e)){
34852             t.onClick(e);
34853             this.fireEvent("click", this, t, e);
34854         }
34855     },
34856
34857     // private
34858     setActiveItem : function(item, autoExpand){
34859         if(item != this.activeItem){
34860             if(this.activeItem){
34861                 this.activeItem.deactivate();
34862             }
34863             this.activeItem = item;
34864             item.activate(autoExpand);
34865         }else if(autoExpand){
34866             item.expandMenu();
34867         }
34868     },
34869
34870     // private
34871     tryActivate : function(start, step){
34872         var items = this.items;
34873         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34874             var item = items.get(i);
34875             if(!item.disabled && item.canActivate){
34876                 this.setActiveItem(item, false);
34877                 return item;
34878             }
34879         }
34880         return false;
34881     },
34882
34883     // private
34884     onMouseOver : function(e){
34885         var t;
34886         if(t = this.findTargetItem(e)){
34887             if(t.canActivate && !t.disabled){
34888                 this.setActiveItem(t, true);
34889             }
34890         }
34891         this.fireEvent("mouseover", this, e, t);
34892     },
34893
34894     // private
34895     onMouseOut : function(e){
34896         var t;
34897         if(t = this.findTargetItem(e)){
34898             if(t == this.activeItem && t.shouldDeactivate(e)){
34899                 this.activeItem.deactivate();
34900                 delete this.activeItem;
34901             }
34902         }
34903         this.fireEvent("mouseout", this, e, t);
34904     },
34905
34906     /**
34907      * Read-only.  Returns true if the menu is currently displayed, else false.
34908      * @type Boolean
34909      */
34910     isVisible : function(){
34911         return this.el && !this.hidden;
34912     },
34913
34914     /**
34915      * Displays this menu relative to another element
34916      * @param {String/HTMLElement/Roo.Element} element The element to align to
34917      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34918      * the element (defaults to this.defaultAlign)
34919      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34920      */
34921     show : function(el, pos, parentMenu){
34922         this.parentMenu = parentMenu;
34923         if(!this.el){
34924             this.render();
34925         }
34926         this.fireEvent("beforeshow", this);
34927         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34928     },
34929
34930     /**
34931      * Displays this menu at a specific xy position
34932      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34933      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34934      */
34935     showAt : function(xy, parentMenu, /* private: */_e){
34936         this.parentMenu = parentMenu;
34937         if(!this.el){
34938             this.render();
34939         }
34940         if(_e !== false){
34941             this.fireEvent("beforeshow", this);
34942             xy = this.el.adjustForConstraints(xy);
34943         }
34944         this.el.setXY(xy);
34945         this.el.show();
34946         this.hidden = false;
34947         this.focus();
34948         this.fireEvent("show", this);
34949     },
34950
34951     focus : function(){
34952         if(!this.hidden){
34953             this.doFocus.defer(50, this);
34954         }
34955     },
34956
34957     doFocus : function(){
34958         if(!this.hidden){
34959             this.focusEl.focus();
34960         }
34961     },
34962
34963     /**
34964      * Hides this menu and optionally all parent menus
34965      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34966      */
34967     hide : function(deep){
34968         if(this.el && this.isVisible()){
34969             this.fireEvent("beforehide", this);
34970             if(this.activeItem){
34971                 this.activeItem.deactivate();
34972                 this.activeItem = null;
34973             }
34974             this.el.hide();
34975             this.hidden = true;
34976             this.fireEvent("hide", this);
34977         }
34978         if(deep === true && this.parentMenu){
34979             this.parentMenu.hide(true);
34980         }
34981     },
34982
34983     /**
34984      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34985      * Any of the following are valid:
34986      * <ul>
34987      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34988      * <li>An HTMLElement object which will be converted to a menu item</li>
34989      * <li>A menu item config object that will be created as a new menu item</li>
34990      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34991      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34992      * </ul>
34993      * Usage:
34994      * <pre><code>
34995 // Create the menu
34996 var menu = new Roo.menu.Menu();
34997
34998 // Create a menu item to add by reference
34999 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35000
35001 // Add a bunch of items at once using different methods.
35002 // Only the last item added will be returned.
35003 var item = menu.add(
35004     menuItem,                // add existing item by ref
35005     'Dynamic Item',          // new TextItem
35006     '-',                     // new separator
35007     { text: 'Config Item' }  // new item by config
35008 );
35009 </code></pre>
35010      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35011      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35012      */
35013     add : function(){
35014         var a = arguments, l = a.length, item;
35015         for(var i = 0; i < l; i++){
35016             var el = a[i];
35017             if ((typeof(el) == "object") && el.xtype && el.xns) {
35018                 el = Roo.factory(el, Roo.menu);
35019             }
35020             
35021             if(el.render){ // some kind of Item
35022                 item = this.addItem(el);
35023             }else if(typeof el == "string"){ // string
35024                 if(el == "separator" || el == "-"){
35025                     item = this.addSeparator();
35026                 }else{
35027                     item = this.addText(el);
35028                 }
35029             }else if(el.tagName || el.el){ // element
35030                 item = this.addElement(el);
35031             }else if(typeof el == "object"){ // must be menu item config?
35032                 item = this.addMenuItem(el);
35033             }
35034         }
35035         return item;
35036     },
35037
35038     /**
35039      * Returns this menu's underlying {@link Roo.Element} object
35040      * @return {Roo.Element} The element
35041      */
35042     getEl : function(){
35043         if(!this.el){
35044             this.render();
35045         }
35046         return this.el;
35047     },
35048
35049     /**
35050      * Adds a separator bar to the menu
35051      * @return {Roo.menu.Item} The menu item that was added
35052      */
35053     addSeparator : function(){
35054         return this.addItem(new Roo.menu.Separator());
35055     },
35056
35057     /**
35058      * Adds an {@link Roo.Element} object to the menu
35059      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35060      * @return {Roo.menu.Item} The menu item that was added
35061      */
35062     addElement : function(el){
35063         return this.addItem(new Roo.menu.BaseItem(el));
35064     },
35065
35066     /**
35067      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35068      * @param {Roo.menu.Item} item The menu item to add
35069      * @return {Roo.menu.Item} The menu item that was added
35070      */
35071     addItem : function(item){
35072         this.items.add(item);
35073         if(this.ul){
35074             var li = document.createElement("li");
35075             li.className = "x-menu-list-item";
35076             this.ul.dom.appendChild(li);
35077             item.render(li, this);
35078             this.delayAutoWidth();
35079         }
35080         return item;
35081     },
35082
35083     /**
35084      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35085      * @param {Object} config A MenuItem config object
35086      * @return {Roo.menu.Item} The menu item that was added
35087      */
35088     addMenuItem : function(config){
35089         if(!(config instanceof Roo.menu.Item)){
35090             if(typeof config.checked == "boolean"){ // must be check menu item config?
35091                 config = new Roo.menu.CheckItem(config);
35092             }else{
35093                 config = new Roo.menu.Item(config);
35094             }
35095         }
35096         return this.addItem(config);
35097     },
35098
35099     /**
35100      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35101      * @param {String} text The text to display in the menu item
35102      * @return {Roo.menu.Item} The menu item that was added
35103      */
35104     addText : function(text){
35105         return this.addItem(new Roo.menu.TextItem({ text : text }));
35106     },
35107
35108     /**
35109      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35110      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35111      * @param {Roo.menu.Item} item The menu item to add
35112      * @return {Roo.menu.Item} The menu item that was added
35113      */
35114     insert : function(index, item){
35115         this.items.insert(index, item);
35116         if(this.ul){
35117             var li = document.createElement("li");
35118             li.className = "x-menu-list-item";
35119             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35120             item.render(li, this);
35121             this.delayAutoWidth();
35122         }
35123         return item;
35124     },
35125
35126     /**
35127      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35128      * @param {Roo.menu.Item} item The menu item to remove
35129      */
35130     remove : function(item){
35131         this.items.removeKey(item.id);
35132         item.destroy();
35133     },
35134
35135     /**
35136      * Removes and destroys all items in the menu
35137      */
35138     removeAll : function(){
35139         var f;
35140         while(f = this.items.first()){
35141             this.remove(f);
35142         }
35143     }
35144 });
35145
35146 // MenuNav is a private utility class used internally by the Menu
35147 Roo.menu.MenuNav = function(menu){
35148     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35149     this.scope = this.menu = menu;
35150 };
35151
35152 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35153     doRelay : function(e, h){
35154         var k = e.getKey();
35155         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35156             this.menu.tryActivate(0, 1);
35157             return false;
35158         }
35159         return h.call(this.scope || this, e, this.menu);
35160     },
35161
35162     up : function(e, m){
35163         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35164             m.tryActivate(m.items.length-1, -1);
35165         }
35166     },
35167
35168     down : function(e, m){
35169         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35170             m.tryActivate(0, 1);
35171         }
35172     },
35173
35174     right : function(e, m){
35175         if(m.activeItem){
35176             m.activeItem.expandMenu(true);
35177         }
35178     },
35179
35180     left : function(e, m){
35181         m.hide();
35182         if(m.parentMenu && m.parentMenu.activeItem){
35183             m.parentMenu.activeItem.activate();
35184         }
35185     },
35186
35187     enter : function(e, m){
35188         if(m.activeItem){
35189             e.stopPropagation();
35190             m.activeItem.onClick(e);
35191             m.fireEvent("click", this, m.activeItem);
35192             return true;
35193         }
35194     }
35195 });/*
35196  * Based on:
35197  * Ext JS Library 1.1.1
35198  * Copyright(c) 2006-2007, Ext JS, LLC.
35199  *
35200  * Originally Released Under LGPL - original licence link has changed is not relivant.
35201  *
35202  * Fork - LGPL
35203  * <script type="text/javascript">
35204  */
35205  
35206 /**
35207  * @class Roo.menu.MenuMgr
35208  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35209  * @singleton
35210  */
35211 Roo.menu.MenuMgr = function(){
35212    var menus, active, groups = {}, attached = false, lastShow = new Date();
35213
35214    // private - called when first menu is created
35215    function init(){
35216        menus = {};
35217        active = new Roo.util.MixedCollection();
35218        Roo.get(document).addKeyListener(27, function(){
35219            if(active.length > 0){
35220                hideAll();
35221            }
35222        });
35223    }
35224
35225    // private
35226    function hideAll(){
35227        if(active && active.length > 0){
35228            var c = active.clone();
35229            c.each(function(m){
35230                m.hide();
35231            });
35232        }
35233    }
35234
35235    // private
35236    function onHide(m){
35237        active.remove(m);
35238        if(active.length < 1){
35239            Roo.get(document).un("mousedown", onMouseDown);
35240            attached = false;
35241        }
35242    }
35243
35244    // private
35245    function onShow(m){
35246        var last = active.last();
35247        lastShow = new Date();
35248        active.add(m);
35249        if(!attached){
35250            Roo.get(document).on("mousedown", onMouseDown);
35251            attached = true;
35252        }
35253        if(m.parentMenu){
35254           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35255           m.parentMenu.activeChild = m;
35256        }else if(last && last.isVisible()){
35257           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35258        }
35259    }
35260
35261    // private
35262    function onBeforeHide(m){
35263        if(m.activeChild){
35264            m.activeChild.hide();
35265        }
35266        if(m.autoHideTimer){
35267            clearTimeout(m.autoHideTimer);
35268            delete m.autoHideTimer;
35269        }
35270    }
35271
35272    // private
35273    function onBeforeShow(m){
35274        var pm = m.parentMenu;
35275        if(!pm && !m.allowOtherMenus){
35276            hideAll();
35277        }else if(pm && pm.activeChild && active != m){
35278            pm.activeChild.hide();
35279        }
35280    }
35281
35282    // private
35283    function onMouseDown(e){
35284        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35285            hideAll();
35286        }
35287    }
35288
35289    // private
35290    function onBeforeCheck(mi, state){
35291        if(state){
35292            var g = groups[mi.group];
35293            for(var i = 0, l = g.length; i < l; i++){
35294                if(g[i] != mi){
35295                    g[i].setChecked(false);
35296                }
35297            }
35298        }
35299    }
35300
35301    return {
35302
35303        /**
35304         * Hides all menus that are currently visible
35305         */
35306        hideAll : function(){
35307             hideAll();  
35308        },
35309
35310        // private
35311        register : function(menu){
35312            if(!menus){
35313                init();
35314            }
35315            menus[menu.id] = menu;
35316            menu.on("beforehide", onBeforeHide);
35317            menu.on("hide", onHide);
35318            menu.on("beforeshow", onBeforeShow);
35319            menu.on("show", onShow);
35320            var g = menu.group;
35321            if(g && menu.events["checkchange"]){
35322                if(!groups[g]){
35323                    groups[g] = [];
35324                }
35325                groups[g].push(menu);
35326                menu.on("checkchange", onCheck);
35327            }
35328        },
35329
35330         /**
35331          * Returns a {@link Roo.menu.Menu} object
35332          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
35333          * be used to generate and return a new Menu instance.
35334          */
35335        get : function(menu){
35336            if(typeof menu == "string"){ // menu id
35337                return menus[menu];
35338            }else if(menu.events){  // menu instance
35339                return menu;
35340            }else if(typeof menu.length == 'number'){ // array of menu items?
35341                return new Roo.menu.Menu({items:menu});
35342            }else{ // otherwise, must be a config
35343                return new Roo.menu.Menu(menu);
35344            }
35345        },
35346
35347        // private
35348        unregister : function(menu){
35349            delete menus[menu.id];
35350            menu.un("beforehide", onBeforeHide);
35351            menu.un("hide", onHide);
35352            menu.un("beforeshow", onBeforeShow);
35353            menu.un("show", onShow);
35354            var g = menu.group;
35355            if(g && menu.events["checkchange"]){
35356                groups[g].remove(menu);
35357                menu.un("checkchange", onCheck);
35358            }
35359        },
35360
35361        // private
35362        registerCheckable : function(menuItem){
35363            var g = menuItem.group;
35364            if(g){
35365                if(!groups[g]){
35366                    groups[g] = [];
35367                }
35368                groups[g].push(menuItem);
35369                menuItem.on("beforecheckchange", onBeforeCheck);
35370            }
35371        },
35372
35373        // private
35374        unregisterCheckable : function(menuItem){
35375            var g = menuItem.group;
35376            if(g){
35377                groups[g].remove(menuItem);
35378                menuItem.un("beforecheckchange", onBeforeCheck);
35379            }
35380        }
35381    };
35382 }();/*
35383  * Based on:
35384  * Ext JS Library 1.1.1
35385  * Copyright(c) 2006-2007, Ext JS, LLC.
35386  *
35387  * Originally Released Under LGPL - original licence link has changed is not relivant.
35388  *
35389  * Fork - LGPL
35390  * <script type="text/javascript">
35391  */
35392  
35393
35394 /**
35395  * @class Roo.menu.BaseItem
35396  * @extends Roo.Component
35397  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
35398  * management and base configuration options shared by all menu components.
35399  * @constructor
35400  * Creates a new BaseItem
35401  * @param {Object} config Configuration options
35402  */
35403 Roo.menu.BaseItem = function(config){
35404     Roo.menu.BaseItem.superclass.constructor.call(this, config);
35405
35406     this.addEvents({
35407         /**
35408          * @event click
35409          * Fires when this item is clicked
35410          * @param {Roo.menu.BaseItem} this
35411          * @param {Roo.EventObject} e
35412          */
35413         click: true,
35414         /**
35415          * @event activate
35416          * Fires when this item is activated
35417          * @param {Roo.menu.BaseItem} this
35418          */
35419         activate : true,
35420         /**
35421          * @event deactivate
35422          * Fires when this item is deactivated
35423          * @param {Roo.menu.BaseItem} this
35424          */
35425         deactivate : true
35426     });
35427
35428     if(this.handler){
35429         this.on("click", this.handler, this.scope, true);
35430     }
35431 };
35432
35433 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
35434     /**
35435      * @cfg {Function} handler
35436      * A function that will handle the click event of this menu item (defaults to undefined)
35437      */
35438     /**
35439      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
35440      */
35441     canActivate : false,
35442     /**
35443      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
35444      */
35445     activeClass : "x-menu-item-active",
35446     /**
35447      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
35448      */
35449     hideOnClick : true,
35450     /**
35451      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
35452      */
35453     hideDelay : 100,
35454
35455     // private
35456     ctype: "Roo.menu.BaseItem",
35457
35458     // private
35459     actionMode : "container",
35460
35461     // private
35462     render : function(container, parentMenu){
35463         this.parentMenu = parentMenu;
35464         Roo.menu.BaseItem.superclass.render.call(this, container);
35465         this.container.menuItemId = this.id;
35466     },
35467
35468     // private
35469     onRender : function(container, position){
35470         this.el = Roo.get(this.el);
35471         container.dom.appendChild(this.el.dom);
35472     },
35473
35474     // private
35475     onClick : function(e){
35476         if(!this.disabled && this.fireEvent("click", this, e) !== false
35477                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
35478             this.handleClick(e);
35479         }else{
35480             e.stopEvent();
35481         }
35482     },
35483
35484     // private
35485     activate : function(){
35486         if(this.disabled){
35487             return false;
35488         }
35489         var li = this.container;
35490         li.addClass(this.activeClass);
35491         this.region = li.getRegion().adjust(2, 2, -2, -2);
35492         this.fireEvent("activate", this);
35493         return true;
35494     },
35495
35496     // private
35497     deactivate : function(){
35498         this.container.removeClass(this.activeClass);
35499         this.fireEvent("deactivate", this);
35500     },
35501
35502     // private
35503     shouldDeactivate : function(e){
35504         return !this.region || !this.region.contains(e.getPoint());
35505     },
35506
35507     // private
35508     handleClick : function(e){
35509         if(this.hideOnClick){
35510             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
35511         }
35512     },
35513
35514     // private
35515     expandMenu : function(autoActivate){
35516         // do nothing
35517     },
35518
35519     // private
35520     hideMenu : function(){
35521         // do nothing
35522     }
35523 });/*
35524  * Based on:
35525  * Ext JS Library 1.1.1
35526  * Copyright(c) 2006-2007, Ext JS, LLC.
35527  *
35528  * Originally Released Under LGPL - original licence link has changed is not relivant.
35529  *
35530  * Fork - LGPL
35531  * <script type="text/javascript">
35532  */
35533  
35534 /**
35535  * @class Roo.menu.Adapter
35536  * @extends Roo.menu.BaseItem
35537  * 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.
35538  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
35539  * @constructor
35540  * Creates a new Adapter
35541  * @param {Object} config Configuration options
35542  */
35543 Roo.menu.Adapter = function(component, config){
35544     Roo.menu.Adapter.superclass.constructor.call(this, config);
35545     this.component = component;
35546 };
35547 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
35548     // private
35549     canActivate : true,
35550
35551     // private
35552     onRender : function(container, position){
35553         this.component.render(container);
35554         this.el = this.component.getEl();
35555     },
35556
35557     // private
35558     activate : function(){
35559         if(this.disabled){
35560             return false;
35561         }
35562         this.component.focus();
35563         this.fireEvent("activate", this);
35564         return true;
35565     },
35566
35567     // private
35568     deactivate : function(){
35569         this.fireEvent("deactivate", this);
35570     },
35571
35572     // private
35573     disable : function(){
35574         this.component.disable();
35575         Roo.menu.Adapter.superclass.disable.call(this);
35576     },
35577
35578     // private
35579     enable : function(){
35580         this.component.enable();
35581         Roo.menu.Adapter.superclass.enable.call(this);
35582     }
35583 });/*
35584  * Based on:
35585  * Ext JS Library 1.1.1
35586  * Copyright(c) 2006-2007, Ext JS, LLC.
35587  *
35588  * Originally Released Under LGPL - original licence link has changed is not relivant.
35589  *
35590  * Fork - LGPL
35591  * <script type="text/javascript">
35592  */
35593
35594 /**
35595  * @class Roo.menu.TextItem
35596  * @extends Roo.menu.BaseItem
35597  * Adds a static text string to a menu, usually used as either a heading or group separator.
35598  * Note: old style constructor with text is still supported.
35599  * 
35600  * @constructor
35601  * Creates a new TextItem
35602  * @param {Object} cfg Configuration
35603  */
35604 Roo.menu.TextItem = function(cfg){
35605     if (typeof(cfg) == 'string') {
35606         this.text = cfg;
35607     } else {
35608         Roo.apply(this,cfg);
35609     }
35610     
35611     Roo.menu.TextItem.superclass.constructor.call(this);
35612 };
35613
35614 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35615     /**
35616      * @cfg {Boolean} text Text to show on item.
35617      */
35618     text : '',
35619     
35620     /**
35621      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35622      */
35623     hideOnClick : false,
35624     /**
35625      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35626      */
35627     itemCls : "x-menu-text",
35628
35629     // private
35630     onRender : function(){
35631         var s = document.createElement("span");
35632         s.className = this.itemCls;
35633         s.innerHTML = this.text;
35634         this.el = s;
35635         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35636     }
35637 });/*
35638  * Based on:
35639  * Ext JS Library 1.1.1
35640  * Copyright(c) 2006-2007, Ext JS, LLC.
35641  *
35642  * Originally Released Under LGPL - original licence link has changed is not relivant.
35643  *
35644  * Fork - LGPL
35645  * <script type="text/javascript">
35646  */
35647
35648 /**
35649  * @class Roo.menu.Separator
35650  * @extends Roo.menu.BaseItem
35651  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35652  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35653  * @constructor
35654  * @param {Object} config Configuration options
35655  */
35656 Roo.menu.Separator = function(config){
35657     Roo.menu.Separator.superclass.constructor.call(this, config);
35658 };
35659
35660 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35661     /**
35662      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35663      */
35664     itemCls : "x-menu-sep",
35665     /**
35666      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35667      */
35668     hideOnClick : false,
35669
35670     // private
35671     onRender : function(li){
35672         var s = document.createElement("span");
35673         s.className = this.itemCls;
35674         s.innerHTML = "&#160;";
35675         this.el = s;
35676         li.addClass("x-menu-sep-li");
35677         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35678     }
35679 });/*
35680  * Based on:
35681  * Ext JS Library 1.1.1
35682  * Copyright(c) 2006-2007, Ext JS, LLC.
35683  *
35684  * Originally Released Under LGPL - original licence link has changed is not relivant.
35685  *
35686  * Fork - LGPL
35687  * <script type="text/javascript">
35688  */
35689 /**
35690  * @class Roo.menu.Item
35691  * @extends Roo.menu.BaseItem
35692  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35693  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35694  * activation and click handling.
35695  * @constructor
35696  * Creates a new Item
35697  * @param {Object} config Configuration options
35698  */
35699 Roo.menu.Item = function(config){
35700     Roo.menu.Item.superclass.constructor.call(this, config);
35701     if(this.menu){
35702         this.menu = Roo.menu.MenuMgr.get(this.menu);
35703     }
35704 };
35705 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35706     
35707     /**
35708      * @cfg {String} text
35709      * The text to show on the menu item.
35710      */
35711     text: '',
35712      /**
35713      * @cfg {String} HTML to render in menu
35714      * The text to show on the menu item (HTML version).
35715      */
35716     html: '',
35717     /**
35718      * @cfg {String} icon
35719      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35720      */
35721     icon: undefined,
35722     /**
35723      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35724      */
35725     itemCls : "x-menu-item",
35726     /**
35727      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35728      */
35729     canActivate : true,
35730     /**
35731      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35732      */
35733     showDelay: 200,
35734     // doc'd in BaseItem
35735     hideDelay: 200,
35736
35737     // private
35738     ctype: "Roo.menu.Item",
35739     
35740     // private
35741     onRender : function(container, position){
35742         var el = document.createElement("a");
35743         el.hideFocus = true;
35744         el.unselectable = "on";
35745         el.href = this.href || "#";
35746         if(this.hrefTarget){
35747             el.target = this.hrefTarget;
35748         }
35749         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35750         
35751         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35752         
35753         el.innerHTML = String.format(
35754                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35755                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35756         this.el = el;
35757         Roo.menu.Item.superclass.onRender.call(this, container, position);
35758     },
35759
35760     /**
35761      * Sets the text to display in this menu item
35762      * @param {String} text The text to display
35763      * @param {Boolean} isHTML true to indicate text is pure html.
35764      */
35765     setText : function(text, isHTML){
35766         if (isHTML) {
35767             this.html = text;
35768         } else {
35769             this.text = text;
35770             this.html = '';
35771         }
35772         if(this.rendered){
35773             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35774      
35775             this.el.update(String.format(
35776                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35777                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35778             this.parentMenu.autoWidth();
35779         }
35780     },
35781
35782     // private
35783     handleClick : function(e){
35784         if(!this.href){ // if no link defined, stop the event automatically
35785             e.stopEvent();
35786         }
35787         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35788     },
35789
35790     // private
35791     activate : function(autoExpand){
35792         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35793             this.focus();
35794             if(autoExpand){
35795                 this.expandMenu();
35796             }
35797         }
35798         return true;
35799     },
35800
35801     // private
35802     shouldDeactivate : function(e){
35803         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35804             if(this.menu && this.menu.isVisible()){
35805                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35806             }
35807             return true;
35808         }
35809         return false;
35810     },
35811
35812     // private
35813     deactivate : function(){
35814         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35815         this.hideMenu();
35816     },
35817
35818     // private
35819     expandMenu : function(autoActivate){
35820         if(!this.disabled && this.menu){
35821             clearTimeout(this.hideTimer);
35822             delete this.hideTimer;
35823             if(!this.menu.isVisible() && !this.showTimer){
35824                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35825             }else if (this.menu.isVisible() && autoActivate){
35826                 this.menu.tryActivate(0, 1);
35827             }
35828         }
35829     },
35830
35831     // private
35832     deferExpand : function(autoActivate){
35833         delete this.showTimer;
35834         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35835         if(autoActivate){
35836             this.menu.tryActivate(0, 1);
35837         }
35838     },
35839
35840     // private
35841     hideMenu : function(){
35842         clearTimeout(this.showTimer);
35843         delete this.showTimer;
35844         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35845             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35846         }
35847     },
35848
35849     // private
35850     deferHide : function(){
35851         delete this.hideTimer;
35852         this.menu.hide();
35853     }
35854 });/*
35855  * Based on:
35856  * Ext JS Library 1.1.1
35857  * Copyright(c) 2006-2007, Ext JS, LLC.
35858  *
35859  * Originally Released Under LGPL - original licence link has changed is not relivant.
35860  *
35861  * Fork - LGPL
35862  * <script type="text/javascript">
35863  */
35864  
35865 /**
35866  * @class Roo.menu.CheckItem
35867  * @extends Roo.menu.Item
35868  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35869  * @constructor
35870  * Creates a new CheckItem
35871  * @param {Object} config Configuration options
35872  */
35873 Roo.menu.CheckItem = function(config){
35874     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35875     this.addEvents({
35876         /**
35877          * @event beforecheckchange
35878          * Fires before the checked value is set, providing an opportunity to cancel if needed
35879          * @param {Roo.menu.CheckItem} this
35880          * @param {Boolean} checked The new checked value that will be set
35881          */
35882         "beforecheckchange" : true,
35883         /**
35884          * @event checkchange
35885          * Fires after the checked value has been set
35886          * @param {Roo.menu.CheckItem} this
35887          * @param {Boolean} checked The checked value that was set
35888          */
35889         "checkchange" : true
35890     });
35891     if(this.checkHandler){
35892         this.on('checkchange', this.checkHandler, this.scope);
35893     }
35894 };
35895 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35896     /**
35897      * @cfg {String} group
35898      * All check items with the same group name will automatically be grouped into a single-select
35899      * radio button group (defaults to '')
35900      */
35901     /**
35902      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35903      */
35904     itemCls : "x-menu-item x-menu-check-item",
35905     /**
35906      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35907      */
35908     groupClass : "x-menu-group-item",
35909
35910     /**
35911      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35912      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35913      * initialized with checked = true will be rendered as checked.
35914      */
35915     checked: false,
35916
35917     // private
35918     ctype: "Roo.menu.CheckItem",
35919
35920     // private
35921     onRender : function(c){
35922         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35923         if(this.group){
35924             this.el.addClass(this.groupClass);
35925         }
35926         Roo.menu.MenuMgr.registerCheckable(this);
35927         if(this.checked){
35928             this.checked = false;
35929             this.setChecked(true, true);
35930         }
35931     },
35932
35933     // private
35934     destroy : function(){
35935         if(this.rendered){
35936             Roo.menu.MenuMgr.unregisterCheckable(this);
35937         }
35938         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35939     },
35940
35941     /**
35942      * Set the checked state of this item
35943      * @param {Boolean} checked The new checked value
35944      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35945      */
35946     setChecked : function(state, suppressEvent){
35947         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35948             if(this.container){
35949                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35950             }
35951             this.checked = state;
35952             if(suppressEvent !== true){
35953                 this.fireEvent("checkchange", this, state);
35954             }
35955         }
35956     },
35957
35958     // private
35959     handleClick : function(e){
35960        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35961            this.setChecked(!this.checked);
35962        }
35963        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35964     }
35965 });/*
35966  * Based on:
35967  * Ext JS Library 1.1.1
35968  * Copyright(c) 2006-2007, Ext JS, LLC.
35969  *
35970  * Originally Released Under LGPL - original licence link has changed is not relivant.
35971  *
35972  * Fork - LGPL
35973  * <script type="text/javascript">
35974  */
35975  
35976 /**
35977  * @class Roo.menu.DateItem
35978  * @extends Roo.menu.Adapter
35979  * A menu item that wraps the {@link Roo.DatPicker} component.
35980  * @constructor
35981  * Creates a new DateItem
35982  * @param {Object} config Configuration options
35983  */
35984 Roo.menu.DateItem = function(config){
35985     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35986     /** The Roo.DatePicker object @type Roo.DatePicker */
35987     this.picker = this.component;
35988     this.addEvents({select: true});
35989     
35990     this.picker.on("render", function(picker){
35991         picker.getEl().swallowEvent("click");
35992         picker.container.addClass("x-menu-date-item");
35993     });
35994
35995     this.picker.on("select", this.onSelect, this);
35996 };
35997
35998 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35999     // private
36000     onSelect : function(picker, date){
36001         this.fireEvent("select", this, date, picker);
36002         Roo.menu.DateItem.superclass.handleClick.call(this);
36003     }
36004 });/*
36005  * Based on:
36006  * Ext JS Library 1.1.1
36007  * Copyright(c) 2006-2007, Ext JS, LLC.
36008  *
36009  * Originally Released Under LGPL - original licence link has changed is not relivant.
36010  *
36011  * Fork - LGPL
36012  * <script type="text/javascript">
36013  */
36014  
36015 /**
36016  * @class Roo.menu.ColorItem
36017  * @extends Roo.menu.Adapter
36018  * A menu item that wraps the {@link Roo.ColorPalette} component.
36019  * @constructor
36020  * Creates a new ColorItem
36021  * @param {Object} config Configuration options
36022  */
36023 Roo.menu.ColorItem = function(config){
36024     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36025     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36026     this.palette = this.component;
36027     this.relayEvents(this.palette, ["select"]);
36028     if(this.selectHandler){
36029         this.on('select', this.selectHandler, this.scope);
36030     }
36031 };
36032 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36033  * Based on:
36034  * Ext JS Library 1.1.1
36035  * Copyright(c) 2006-2007, Ext JS, LLC.
36036  *
36037  * Originally Released Under LGPL - original licence link has changed is not relivant.
36038  *
36039  * Fork - LGPL
36040  * <script type="text/javascript">
36041  */
36042  
36043
36044 /**
36045  * @class Roo.menu.DateMenu
36046  * @extends Roo.menu.Menu
36047  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36048  * @constructor
36049  * Creates a new DateMenu
36050  * @param {Object} config Configuration options
36051  */
36052 Roo.menu.DateMenu = function(config){
36053     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36054     this.plain = true;
36055     var di = new Roo.menu.DateItem(config);
36056     this.add(di);
36057     /**
36058      * The {@link Roo.DatePicker} instance for this DateMenu
36059      * @type DatePicker
36060      */
36061     this.picker = di.picker;
36062     /**
36063      * @event select
36064      * @param {DatePicker} picker
36065      * @param {Date} date
36066      */
36067     this.relayEvents(di, ["select"]);
36068     this.on('beforeshow', function(){
36069         if(this.picker){
36070             this.picker.hideMonthPicker(false);
36071         }
36072     }, this);
36073 };
36074 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36075     cls:'x-date-menu'
36076 });/*
36077  * Based on:
36078  * Ext JS Library 1.1.1
36079  * Copyright(c) 2006-2007, Ext JS, LLC.
36080  *
36081  * Originally Released Under LGPL - original licence link has changed is not relivant.
36082  *
36083  * Fork - LGPL
36084  * <script type="text/javascript">
36085  */
36086  
36087
36088 /**
36089  * @class Roo.menu.ColorMenu
36090  * @extends Roo.menu.Menu
36091  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36092  * @constructor
36093  * Creates a new ColorMenu
36094  * @param {Object} config Configuration options
36095  */
36096 Roo.menu.ColorMenu = function(config){
36097     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36098     this.plain = true;
36099     var ci = new Roo.menu.ColorItem(config);
36100     this.add(ci);
36101     /**
36102      * The {@link Roo.ColorPalette} instance for this ColorMenu
36103      * @type ColorPalette
36104      */
36105     this.palette = ci.palette;
36106     /**
36107      * @event select
36108      * @param {ColorPalette} palette
36109      * @param {String} color
36110      */
36111     this.relayEvents(ci, ["select"]);
36112 };
36113 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36114  * Based on:
36115  * Ext JS Library 1.1.1
36116  * Copyright(c) 2006-2007, Ext JS, LLC.
36117  *
36118  * Originally Released Under LGPL - original licence link has changed is not relivant.
36119  *
36120  * Fork - LGPL
36121  * <script type="text/javascript">
36122  */
36123  
36124 /**
36125  * @class Roo.form.Field
36126  * @extends Roo.BoxComponent
36127  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36128  * @constructor
36129  * Creates a new Field
36130  * @param {Object} config Configuration options
36131  */
36132 Roo.form.Field = function(config){
36133     Roo.form.Field.superclass.constructor.call(this, config);
36134 };
36135
36136 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36137     /**
36138      * @cfg {String} fieldLabel Label to use when rendering a form.
36139      */
36140        /**
36141      * @cfg {String} qtip Mouse over tip
36142      */
36143      
36144     /**
36145      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36146      */
36147     invalidClass : "x-form-invalid",
36148     /**
36149      * @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")
36150      */
36151     invalidText : "The value in this field is invalid",
36152     /**
36153      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36154      */
36155     focusClass : "x-form-focus",
36156     /**
36157      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36158       automatic validation (defaults to "keyup").
36159      */
36160     validationEvent : "keyup",
36161     /**
36162      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36163      */
36164     validateOnBlur : true,
36165     /**
36166      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36167      */
36168     validationDelay : 250,
36169     /**
36170      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36171      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36172      */
36173     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36174     /**
36175      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36176      */
36177     fieldClass : "x-form-field",
36178     /**
36179      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36180      *<pre>
36181 Value         Description
36182 -----------   ----------------------------------------------------------------------
36183 qtip          Display a quick tip when the user hovers over the field
36184 title         Display a default browser title attribute popup
36185 under         Add a block div beneath the field containing the error text
36186 side          Add an error icon to the right of the field with a popup on hover
36187 [element id]  Add the error text directly to the innerHTML of the specified element
36188 </pre>
36189      */
36190     msgTarget : 'qtip',
36191     /**
36192      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36193      */
36194     msgFx : 'normal',
36195
36196     /**
36197      * @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.
36198      */
36199     readOnly : false,
36200
36201     /**
36202      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36203      */
36204     disabled : false,
36205
36206     /**
36207      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36208      */
36209     inputType : undefined,
36210     
36211     /**
36212      * @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).
36213          */
36214         tabIndex : undefined,
36215         
36216     // private
36217     isFormField : true,
36218
36219     // private
36220     hasFocus : false,
36221     /**
36222      * @property {Roo.Element} fieldEl
36223      * Element Containing the rendered Field (with label etc.)
36224      */
36225     /**
36226      * @cfg {Mixed} value A value to initialize this field with.
36227      */
36228     value : undefined,
36229
36230     /**
36231      * @cfg {String} name The field's HTML name attribute.
36232      */
36233     /**
36234      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36235      */
36236
36237         // private ??
36238         initComponent : function(){
36239         Roo.form.Field.superclass.initComponent.call(this);
36240         this.addEvents({
36241             /**
36242              * @event focus
36243              * Fires when this field receives input focus.
36244              * @param {Roo.form.Field} this
36245              */
36246             focus : true,
36247             /**
36248              * @event blur
36249              * Fires when this field loses input focus.
36250              * @param {Roo.form.Field} this
36251              */
36252             blur : true,
36253             /**
36254              * @event specialkey
36255              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36256              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36257              * @param {Roo.form.Field} this
36258              * @param {Roo.EventObject} e The event object
36259              */
36260             specialkey : true,
36261             /**
36262              * @event change
36263              * Fires just before the field blurs if the field value has changed.
36264              * @param {Roo.form.Field} this
36265              * @param {Mixed} newValue The new value
36266              * @param {Mixed} oldValue The original value
36267              */
36268             change : true,
36269             /**
36270              * @event invalid
36271              * Fires after the field has been marked as invalid.
36272              * @param {Roo.form.Field} this
36273              * @param {String} msg The validation message
36274              */
36275             invalid : true,
36276             /**
36277              * @event valid
36278              * Fires after the field has been validated with no errors.
36279              * @param {Roo.form.Field} this
36280              */
36281             valid : true,
36282              /**
36283              * @event keyup
36284              * Fires after the key up
36285              * @param {Roo.form.Field} this
36286              * @param {Roo.EventObject}  e The event Object
36287              */
36288             keyup : true
36289         });
36290     },
36291
36292     /**
36293      * Returns the name attribute of the field if available
36294      * @return {String} name The field name
36295      */
36296     getName: function(){
36297          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36298     },
36299
36300     // private
36301     onRender : function(ct, position){
36302         Roo.form.Field.superclass.onRender.call(this, ct, position);
36303         if(!this.el){
36304             var cfg = this.getAutoCreate();
36305             if(!cfg.name){
36306                 cfg.name = this.name || this.id;
36307             }
36308             if(this.inputType){
36309                 cfg.type = this.inputType;
36310             }
36311             this.el = ct.createChild(cfg, position);
36312         }
36313         var type = this.el.dom.type;
36314         if(type){
36315             if(type == 'password'){
36316                 type = 'text';
36317             }
36318             this.el.addClass('x-form-'+type);
36319         }
36320         if(this.readOnly){
36321             this.el.dom.readOnly = true;
36322         }
36323         if(this.tabIndex !== undefined){
36324             this.el.dom.setAttribute('tabIndex', this.tabIndex);
36325         }
36326
36327         this.el.addClass([this.fieldClass, this.cls]);
36328         this.initValue();
36329     },
36330
36331     /**
36332      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
36333      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
36334      * @return {Roo.form.Field} this
36335      */
36336     applyTo : function(target){
36337         this.allowDomMove = false;
36338         this.el = Roo.get(target);
36339         this.render(this.el.dom.parentNode);
36340         return this;
36341     },
36342
36343     // private
36344     initValue : function(){
36345         if(this.value !== undefined){
36346             this.setValue(this.value);
36347         }else if(this.el.dom.value.length > 0){
36348             this.setValue(this.el.dom.value);
36349         }
36350     },
36351
36352     /**
36353      * Returns true if this field has been changed since it was originally loaded and is not disabled.
36354      */
36355     isDirty : function() {
36356         if(this.disabled) {
36357             return false;
36358         }
36359         return String(this.getValue()) !== String(this.originalValue);
36360     },
36361
36362     // private
36363     afterRender : function(){
36364         Roo.form.Field.superclass.afterRender.call(this);
36365         this.initEvents();
36366     },
36367
36368     // private
36369     fireKey : function(e){
36370         //Roo.log('field ' + e.getKey());
36371         if(e.isNavKeyPress()){
36372             this.fireEvent("specialkey", this, e);
36373         }
36374     },
36375
36376     /**
36377      * Resets the current field value to the originally loaded value and clears any validation messages
36378      */
36379     reset : function(){
36380         this.setValue(this.originalValue);
36381         this.clearInvalid();
36382     },
36383
36384     // private
36385     initEvents : function(){
36386         // safari killled keypress - so keydown is now used..
36387         this.el.on("keydown" , this.fireKey,  this);
36388         this.el.on("focus", this.onFocus,  this);
36389         this.el.on("blur", this.onBlur,  this);
36390         this.el.relayEvent('keyup', this);
36391
36392         // reference to original value for reset
36393         this.originalValue = this.getValue();
36394     },
36395
36396     // private
36397     onFocus : function(){
36398         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36399             this.el.addClass(this.focusClass);
36400         }
36401         if(!this.hasFocus){
36402             this.hasFocus = true;
36403             this.startValue = this.getValue();
36404             this.fireEvent("focus", this);
36405         }
36406     },
36407
36408     beforeBlur : Roo.emptyFn,
36409
36410     // private
36411     onBlur : function(){
36412         this.beforeBlur();
36413         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36414             this.el.removeClass(this.focusClass);
36415         }
36416         this.hasFocus = false;
36417         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
36418             this.validate();
36419         }
36420         var v = this.getValue();
36421         if(String(v) !== String(this.startValue)){
36422             this.fireEvent('change', this, v, this.startValue);
36423         }
36424         this.fireEvent("blur", this);
36425     },
36426
36427     /**
36428      * Returns whether or not the field value is currently valid
36429      * @param {Boolean} preventMark True to disable marking the field invalid
36430      * @return {Boolean} True if the value is valid, else false
36431      */
36432     isValid : function(preventMark){
36433         if(this.disabled){
36434             return true;
36435         }
36436         var restore = this.preventMark;
36437         this.preventMark = preventMark === true;
36438         var v = this.validateValue(this.processValue(this.getRawValue()));
36439         this.preventMark = restore;
36440         return v;
36441     },
36442
36443     /**
36444      * Validates the field value
36445      * @return {Boolean} True if the value is valid, else false
36446      */
36447     validate : function(){
36448         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
36449             this.clearInvalid();
36450             return true;
36451         }
36452         return false;
36453     },
36454
36455     processValue : function(value){
36456         return value;
36457     },
36458
36459     // private
36460     // Subclasses should provide the validation implementation by overriding this
36461     validateValue : function(value){
36462         return true;
36463     },
36464
36465     /**
36466      * Mark this field as invalid
36467      * @param {String} msg The validation message
36468      */
36469     markInvalid : function(msg){
36470         if(!this.rendered || this.preventMark){ // not rendered
36471             return;
36472         }
36473         this.el.addClass(this.invalidClass);
36474         msg = msg || this.invalidText;
36475         switch(this.msgTarget){
36476             case 'qtip':
36477                 this.el.dom.qtip = msg;
36478                 this.el.dom.qclass = 'x-form-invalid-tip';
36479                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
36480                     Roo.QuickTips.enable();
36481                 }
36482                 break;
36483             case 'title':
36484                 this.el.dom.title = msg;
36485                 break;
36486             case 'under':
36487                 if(!this.errorEl){
36488                     var elp = this.el.findParent('.x-form-element', 5, true);
36489                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
36490                     this.errorEl.setWidth(elp.getWidth(true)-20);
36491                 }
36492                 this.errorEl.update(msg);
36493                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
36494                 break;
36495             case 'side':
36496                 if(!this.errorIcon){
36497                     var elp = this.el.findParent('.x-form-element', 5, true);
36498                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
36499                 }
36500                 this.alignErrorIcon();
36501                 this.errorIcon.dom.qtip = msg;
36502                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
36503                 this.errorIcon.show();
36504                 this.on('resize', this.alignErrorIcon, this);
36505                 break;
36506             default:
36507                 var t = Roo.getDom(this.msgTarget);
36508                 t.innerHTML = msg;
36509                 t.style.display = this.msgDisplay;
36510                 break;
36511         }
36512         this.fireEvent('invalid', this, msg);
36513     },
36514
36515     // private
36516     alignErrorIcon : function(){
36517         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
36518     },
36519
36520     /**
36521      * Clear any invalid styles/messages for this field
36522      */
36523     clearInvalid : function(){
36524         if(!this.rendered || this.preventMark){ // not rendered
36525             return;
36526         }
36527         this.el.removeClass(this.invalidClass);
36528         switch(this.msgTarget){
36529             case 'qtip':
36530                 this.el.dom.qtip = '';
36531                 break;
36532             case 'title':
36533                 this.el.dom.title = '';
36534                 break;
36535             case 'under':
36536                 if(this.errorEl){
36537                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
36538                 }
36539                 break;
36540             case 'side':
36541                 if(this.errorIcon){
36542                     this.errorIcon.dom.qtip = '';
36543                     this.errorIcon.hide();
36544                     this.un('resize', this.alignErrorIcon, this);
36545                 }
36546                 break;
36547             default:
36548                 var t = Roo.getDom(this.msgTarget);
36549                 t.innerHTML = '';
36550                 t.style.display = 'none';
36551                 break;
36552         }
36553         this.fireEvent('valid', this);
36554     },
36555
36556     /**
36557      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
36558      * @return {Mixed} value The field value
36559      */
36560     getRawValue : function(){
36561         var v = this.el.getValue();
36562         if(v === this.emptyText){
36563             v = '';
36564         }
36565         return v;
36566     },
36567
36568     /**
36569      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
36570      * @return {Mixed} value The field value
36571      */
36572     getValue : function(){
36573         var v = this.el.getValue();
36574         if(v === this.emptyText || v === undefined){
36575             v = '';
36576         }
36577         return v;
36578     },
36579
36580     /**
36581      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
36582      * @param {Mixed} value The value to set
36583      */
36584     setRawValue : function(v){
36585         return this.el.dom.value = (v === null || v === undefined ? '' : v);
36586     },
36587
36588     /**
36589      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
36590      * @param {Mixed} value The value to set
36591      */
36592     setValue : function(v){
36593         this.value = v;
36594         if(this.rendered){
36595             this.el.dom.value = (v === null || v === undefined ? '' : v);
36596              this.validate();
36597         }
36598     },
36599
36600     adjustSize : function(w, h){
36601         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
36602         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
36603         return s;
36604     },
36605
36606     adjustWidth : function(tag, w){
36607         tag = tag.toLowerCase();
36608         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36609             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36610                 if(tag == 'input'){
36611                     return w + 2;
36612                 }
36613                 if(tag = 'textarea'){
36614                     return w-2;
36615                 }
36616             }else if(Roo.isOpera){
36617                 if(tag == 'input'){
36618                     return w + 2;
36619                 }
36620                 if(tag = 'textarea'){
36621                     return w-2;
36622                 }
36623             }
36624         }
36625         return w;
36626     }
36627 });
36628
36629
36630 // anything other than normal should be considered experimental
36631 Roo.form.Field.msgFx = {
36632     normal : {
36633         show: function(msgEl, f){
36634             msgEl.setDisplayed('block');
36635         },
36636
36637         hide : function(msgEl, f){
36638             msgEl.setDisplayed(false).update('');
36639         }
36640     },
36641
36642     slide : {
36643         show: function(msgEl, f){
36644             msgEl.slideIn('t', {stopFx:true});
36645         },
36646
36647         hide : function(msgEl, f){
36648             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36649         }
36650     },
36651
36652     slideRight : {
36653         show: function(msgEl, f){
36654             msgEl.fixDisplay();
36655             msgEl.alignTo(f.el, 'tl-tr');
36656             msgEl.slideIn('l', {stopFx:true});
36657         },
36658
36659         hide : function(msgEl, f){
36660             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36661         }
36662     }
36663 };/*
36664  * Based on:
36665  * Ext JS Library 1.1.1
36666  * Copyright(c) 2006-2007, Ext JS, LLC.
36667  *
36668  * Originally Released Under LGPL - original licence link has changed is not relivant.
36669  *
36670  * Fork - LGPL
36671  * <script type="text/javascript">
36672  */
36673  
36674
36675 /**
36676  * @class Roo.form.TextField
36677  * @extends Roo.form.Field
36678  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36679  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36680  * @constructor
36681  * Creates a new TextField
36682  * @param {Object} config Configuration options
36683  */
36684 Roo.form.TextField = function(config){
36685     Roo.form.TextField.superclass.constructor.call(this, config);
36686     this.addEvents({
36687         /**
36688          * @event autosize
36689          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36690          * according to the default logic, but this event provides a hook for the developer to apply additional
36691          * logic at runtime to resize the field if needed.
36692              * @param {Roo.form.Field} this This text field
36693              * @param {Number} width The new field width
36694              */
36695         autosize : true
36696     });
36697 };
36698
36699 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36700     /**
36701      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36702      */
36703     grow : false,
36704     /**
36705      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36706      */
36707     growMin : 30,
36708     /**
36709      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36710      */
36711     growMax : 800,
36712     /**
36713      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36714      */
36715     vtype : null,
36716     /**
36717      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36718      */
36719     maskRe : null,
36720     /**
36721      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36722      */
36723     disableKeyFilter : false,
36724     /**
36725      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36726      */
36727     allowBlank : true,
36728     /**
36729      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36730      */
36731     minLength : 0,
36732     /**
36733      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36734      */
36735     maxLength : Number.MAX_VALUE,
36736     /**
36737      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36738      */
36739     minLengthText : "The minimum length for this field is {0}",
36740     /**
36741      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36742      */
36743     maxLengthText : "The maximum length for this field is {0}",
36744     /**
36745      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36746      */
36747     selectOnFocus : false,
36748     /**
36749      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36750      */
36751     blankText : "This field is required",
36752     /**
36753      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36754      * If available, this function will be called only after the basic validators all return true, and will be passed the
36755      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36756      */
36757     validator : null,
36758     /**
36759      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36760      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36761      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36762      */
36763     regex : null,
36764     /**
36765      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36766      */
36767     regexText : "",
36768     /**
36769      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36770      */
36771     emptyText : null,
36772     /**
36773      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36774      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36775      */
36776     emptyClass : 'x-form-empty-field',
36777
36778     // private
36779     initEvents : function(){
36780         Roo.form.TextField.superclass.initEvents.call(this);
36781         if(this.validationEvent == 'keyup'){
36782             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36783             this.el.on('keyup', this.filterValidation, this);
36784         }
36785         else if(this.validationEvent !== false){
36786             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36787         }
36788         if(this.selectOnFocus || this.emptyText){
36789             this.on("focus", this.preFocus, this);
36790             if(this.emptyText){
36791                 this.on('blur', this.postBlur, this);
36792                 this.applyEmptyText();
36793             }
36794         }
36795         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36796             this.el.on("keypress", this.filterKeys, this);
36797         }
36798         if(this.grow){
36799             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36800             this.el.on("click", this.autoSize,  this);
36801         }
36802     },
36803
36804     processValue : function(value){
36805         if(this.stripCharsRe){
36806             var newValue = value.replace(this.stripCharsRe, '');
36807             if(newValue !== value){
36808                 this.setRawValue(newValue);
36809                 return newValue;
36810             }
36811         }
36812         return value;
36813     },
36814
36815     filterValidation : function(e){
36816         if(!e.isNavKeyPress()){
36817             this.validationTask.delay(this.validationDelay);
36818         }
36819     },
36820
36821     // private
36822     onKeyUp : function(e){
36823         if(!e.isNavKeyPress()){
36824             this.autoSize();
36825         }
36826     },
36827
36828     /**
36829      * Resets the current field value to the originally-loaded value and clears any validation messages.
36830      * Also adds emptyText and emptyClass if the original value was blank.
36831      */
36832     reset : function(){
36833         Roo.form.TextField.superclass.reset.call(this);
36834         this.applyEmptyText();
36835     },
36836
36837     applyEmptyText : function(){
36838         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36839             this.setRawValue(this.emptyText);
36840             this.el.addClass(this.emptyClass);
36841         }
36842     },
36843
36844     // private
36845     preFocus : function(){
36846         if(this.emptyText){
36847             if(this.el.dom.value == this.emptyText){
36848                 this.setRawValue('');
36849             }
36850             this.el.removeClass(this.emptyClass);
36851         }
36852         if(this.selectOnFocus){
36853             this.el.dom.select();
36854         }
36855     },
36856
36857     // private
36858     postBlur : function(){
36859         this.applyEmptyText();
36860     },
36861
36862     // private
36863     filterKeys : function(e){
36864         var k = e.getKey();
36865         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36866             return;
36867         }
36868         var c = e.getCharCode(), cc = String.fromCharCode(c);
36869         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36870             return;
36871         }
36872         if(!this.maskRe.test(cc)){
36873             e.stopEvent();
36874         }
36875     },
36876
36877     setValue : function(v){
36878         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36879             this.el.removeClass(this.emptyClass);
36880         }
36881         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36882         this.applyEmptyText();
36883         this.autoSize();
36884     },
36885
36886     /**
36887      * Validates a value according to the field's validation rules and marks the field as invalid
36888      * if the validation fails
36889      * @param {Mixed} value The value to validate
36890      * @return {Boolean} True if the value is valid, else false
36891      */
36892     validateValue : function(value){
36893         if(value.length < 1 || value === this.emptyText){ // if it's blank
36894              if(this.allowBlank){
36895                 this.clearInvalid();
36896                 return true;
36897              }else{
36898                 this.markInvalid(this.blankText);
36899                 return false;
36900              }
36901         }
36902         if(value.length < this.minLength){
36903             this.markInvalid(String.format(this.minLengthText, this.minLength));
36904             return false;
36905         }
36906         if(value.length > this.maxLength){
36907             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36908             return false;
36909         }
36910         if(this.vtype){
36911             var vt = Roo.form.VTypes;
36912             if(!vt[this.vtype](value, this)){
36913                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36914                 return false;
36915             }
36916         }
36917         if(typeof this.validator == "function"){
36918             var msg = this.validator(value);
36919             if(msg !== true){
36920                 this.markInvalid(msg);
36921                 return false;
36922             }
36923         }
36924         if(this.regex && !this.regex.test(value)){
36925             this.markInvalid(this.regexText);
36926             return false;
36927         }
36928         return true;
36929     },
36930
36931     /**
36932      * Selects text in this field
36933      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36934      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36935      */
36936     selectText : function(start, end){
36937         var v = this.getRawValue();
36938         if(v.length > 0){
36939             start = start === undefined ? 0 : start;
36940             end = end === undefined ? v.length : end;
36941             var d = this.el.dom;
36942             if(d.setSelectionRange){
36943                 d.setSelectionRange(start, end);
36944             }else if(d.createTextRange){
36945                 var range = d.createTextRange();
36946                 range.moveStart("character", start);
36947                 range.moveEnd("character", v.length-end);
36948                 range.select();
36949             }
36950         }
36951     },
36952
36953     /**
36954      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36955      * This only takes effect if grow = true, and fires the autosize event.
36956      */
36957     autoSize : function(){
36958         if(!this.grow || !this.rendered){
36959             return;
36960         }
36961         if(!this.metrics){
36962             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36963         }
36964         var el = this.el;
36965         var v = el.dom.value;
36966         var d = document.createElement('div');
36967         d.appendChild(document.createTextNode(v));
36968         v = d.innerHTML;
36969         d = null;
36970         v += "&#160;";
36971         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36972         this.el.setWidth(w);
36973         this.fireEvent("autosize", this, w);
36974     }
36975 });/*
36976  * Based on:
36977  * Ext JS Library 1.1.1
36978  * Copyright(c) 2006-2007, Ext JS, LLC.
36979  *
36980  * Originally Released Under LGPL - original licence link has changed is not relivant.
36981  *
36982  * Fork - LGPL
36983  * <script type="text/javascript">
36984  */
36985  
36986 /**
36987  * @class Roo.form.Hidden
36988  * @extends Roo.form.TextField
36989  * Simple Hidden element used on forms 
36990  * 
36991  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36992  * 
36993  * @constructor
36994  * Creates a new Hidden form element.
36995  * @param {Object} config Configuration options
36996  */
36997
36998
36999
37000 // easy hidden field...
37001 Roo.form.Hidden = function(config){
37002     Roo.form.Hidden.superclass.constructor.call(this, config);
37003 };
37004   
37005 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37006     fieldLabel:      '',
37007     inputType:      'hidden',
37008     width:          50,
37009     allowBlank:     true,
37010     labelSeparator: '',
37011     hidden:         true,
37012     itemCls :       'x-form-item-display-none'
37013
37014
37015 });
37016
37017
37018 /*
37019  * Based on:
37020  * Ext JS Library 1.1.1
37021  * Copyright(c) 2006-2007, Ext JS, LLC.
37022  *
37023  * Originally Released Under LGPL - original licence link has changed is not relivant.
37024  *
37025  * Fork - LGPL
37026  * <script type="text/javascript">
37027  */
37028  
37029 /**
37030  * @class Roo.form.TriggerField
37031  * @extends Roo.form.TextField
37032  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37033  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37034  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37035  * for which you can provide a custom implementation.  For example:
37036  * <pre><code>
37037 var trigger = new Roo.form.TriggerField();
37038 trigger.onTriggerClick = myTriggerFn;
37039 trigger.applyTo('my-field');
37040 </code></pre>
37041  *
37042  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37043  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37044  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37045  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37046  * @constructor
37047  * Create a new TriggerField.
37048  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37049  * to the base TextField)
37050  */
37051 Roo.form.TriggerField = function(config){
37052     this.mimicing = false;
37053     Roo.form.TriggerField.superclass.constructor.call(this, config);
37054 };
37055
37056 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37057     /**
37058      * @cfg {String} triggerClass A CSS class to apply to the trigger
37059      */
37060     /**
37061      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37062      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37063      */
37064     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37065     /**
37066      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37067      */
37068     hideTrigger:false,
37069
37070     /** @cfg {Boolean} grow @hide */
37071     /** @cfg {Number} growMin @hide */
37072     /** @cfg {Number} growMax @hide */
37073
37074     /**
37075      * @hide 
37076      * @method
37077      */
37078     autoSize: Roo.emptyFn,
37079     // private
37080     monitorTab : true,
37081     // private
37082     deferHeight : true,
37083
37084     
37085     actionMode : 'wrap',
37086     // private
37087     onResize : function(w, h){
37088         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37089         if(typeof w == 'number'){
37090             var x = w - this.trigger.getWidth();
37091             this.el.setWidth(this.adjustWidth('input', x));
37092             this.trigger.setStyle('left', x+'px');
37093         }
37094     },
37095
37096     // private
37097     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37098
37099     // private
37100     getResizeEl : function(){
37101         return this.wrap;
37102     },
37103
37104     // private
37105     getPositionEl : function(){
37106         return this.wrap;
37107     },
37108
37109     // private
37110     alignErrorIcon : function(){
37111         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37112     },
37113
37114     // private
37115     onRender : function(ct, position){
37116         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37117         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37118         this.trigger = this.wrap.createChild(this.triggerConfig ||
37119                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37120         if(this.hideTrigger){
37121             this.trigger.setDisplayed(false);
37122         }
37123         this.initTrigger();
37124         if(!this.width){
37125             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37126         }
37127     },
37128
37129     // private
37130     initTrigger : function(){
37131         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37132         this.trigger.addClassOnOver('x-form-trigger-over');
37133         this.trigger.addClassOnClick('x-form-trigger-click');
37134     },
37135
37136     // private
37137     onDestroy : function(){
37138         if(this.trigger){
37139             this.trigger.removeAllListeners();
37140             this.trigger.remove();
37141         }
37142         if(this.wrap){
37143             this.wrap.remove();
37144         }
37145         Roo.form.TriggerField.superclass.onDestroy.call(this);
37146     },
37147
37148     // private
37149     onFocus : function(){
37150         Roo.form.TriggerField.superclass.onFocus.call(this);
37151         if(!this.mimicing){
37152             this.wrap.addClass('x-trigger-wrap-focus');
37153             this.mimicing = true;
37154             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37155             if(this.monitorTab){
37156                 this.el.on("keydown", this.checkTab, this);
37157             }
37158         }
37159     },
37160
37161     // private
37162     checkTab : function(e){
37163         if(e.getKey() == e.TAB){
37164             this.triggerBlur();
37165         }
37166     },
37167
37168     // private
37169     onBlur : function(){
37170         // do nothing
37171     },
37172
37173     // private
37174     mimicBlur : function(e, t){
37175         if(!this.wrap.contains(t) && this.validateBlur()){
37176             this.triggerBlur();
37177         }
37178     },
37179
37180     // private
37181     triggerBlur : function(){
37182         this.mimicing = false;
37183         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37184         if(this.monitorTab){
37185             this.el.un("keydown", this.checkTab, this);
37186         }
37187         this.wrap.removeClass('x-trigger-wrap-focus');
37188         Roo.form.TriggerField.superclass.onBlur.call(this);
37189     },
37190
37191     // private
37192     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37193     validateBlur : function(e, t){
37194         return true;
37195     },
37196
37197     // private
37198     onDisable : function(){
37199         Roo.form.TriggerField.superclass.onDisable.call(this);
37200         if(this.wrap){
37201             this.wrap.addClass('x-item-disabled');
37202         }
37203     },
37204
37205     // private
37206     onEnable : function(){
37207         Roo.form.TriggerField.superclass.onEnable.call(this);
37208         if(this.wrap){
37209             this.wrap.removeClass('x-item-disabled');
37210         }
37211     },
37212
37213     // private
37214     onShow : function(){
37215         var ae = this.getActionEl();
37216         
37217         if(ae){
37218             ae.dom.style.display = '';
37219             ae.dom.style.visibility = 'visible';
37220         }
37221     },
37222
37223     // private
37224     
37225     onHide : function(){
37226         var ae = this.getActionEl();
37227         ae.dom.style.display = 'none';
37228     },
37229
37230     /**
37231      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37232      * by an implementing function.
37233      * @method
37234      * @param {EventObject} e
37235      */
37236     onTriggerClick : Roo.emptyFn
37237 });
37238
37239 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37240 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37241 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37242 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37243     initComponent : function(){
37244         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37245
37246         this.triggerConfig = {
37247             tag:'span', cls:'x-form-twin-triggers', cn:[
37248             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37249             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37250         ]};
37251     },
37252
37253     getTrigger : function(index){
37254         return this.triggers[index];
37255     },
37256
37257     initTrigger : function(){
37258         var ts = this.trigger.select('.x-form-trigger', true);
37259         this.wrap.setStyle('overflow', 'hidden');
37260         var triggerField = this;
37261         ts.each(function(t, all, index){
37262             t.hide = function(){
37263                 var w = triggerField.wrap.getWidth();
37264                 this.dom.style.display = 'none';
37265                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37266             };
37267             t.show = function(){
37268                 var w = triggerField.wrap.getWidth();
37269                 this.dom.style.display = '';
37270                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37271             };
37272             var triggerIndex = 'Trigger'+(index+1);
37273
37274             if(this['hide'+triggerIndex]){
37275                 t.dom.style.display = 'none';
37276             }
37277             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37278             t.addClassOnOver('x-form-trigger-over');
37279             t.addClassOnClick('x-form-trigger-click');
37280         }, this);
37281         this.triggers = ts.elements;
37282     },
37283
37284     onTrigger1Click : Roo.emptyFn,
37285     onTrigger2Click : Roo.emptyFn
37286 });/*
37287  * Based on:
37288  * Ext JS Library 1.1.1
37289  * Copyright(c) 2006-2007, Ext JS, LLC.
37290  *
37291  * Originally Released Under LGPL - original licence link has changed is not relivant.
37292  *
37293  * Fork - LGPL
37294  * <script type="text/javascript">
37295  */
37296  
37297 /**
37298  * @class Roo.form.TextArea
37299  * @extends Roo.form.TextField
37300  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37301  * support for auto-sizing.
37302  * @constructor
37303  * Creates a new TextArea
37304  * @param {Object} config Configuration options
37305  */
37306 Roo.form.TextArea = function(config){
37307     Roo.form.TextArea.superclass.constructor.call(this, config);
37308     // these are provided exchanges for backwards compat
37309     // minHeight/maxHeight were replaced by growMin/growMax to be
37310     // compatible with TextField growing config values
37311     if(this.minHeight !== undefined){
37312         this.growMin = this.minHeight;
37313     }
37314     if(this.maxHeight !== undefined){
37315         this.growMax = this.maxHeight;
37316     }
37317 };
37318
37319 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
37320     /**
37321      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
37322      */
37323     growMin : 60,
37324     /**
37325      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
37326      */
37327     growMax: 1000,
37328     /**
37329      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
37330      * in the field (equivalent to setting overflow: hidden, defaults to false)
37331      */
37332     preventScrollbars: false,
37333     /**
37334      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37335      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
37336      */
37337
37338     // private
37339     onRender : function(ct, position){
37340         if(!this.el){
37341             this.defaultAutoCreate = {
37342                 tag: "textarea",
37343                 style:"width:300px;height:60px;",
37344                 autocomplete: "off"
37345             };
37346         }
37347         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
37348         if(this.grow){
37349             this.textSizeEl = Roo.DomHelper.append(document.body, {
37350                 tag: "pre", cls: "x-form-grow-sizer"
37351             });
37352             if(this.preventScrollbars){
37353                 this.el.setStyle("overflow", "hidden");
37354             }
37355             this.el.setHeight(this.growMin);
37356         }
37357     },
37358
37359     onDestroy : function(){
37360         if(this.textSizeEl){
37361             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
37362         }
37363         Roo.form.TextArea.superclass.onDestroy.call(this);
37364     },
37365
37366     // private
37367     onKeyUp : function(e){
37368         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
37369             this.autoSize();
37370         }
37371     },
37372
37373     /**
37374      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
37375      * This only takes effect if grow = true, and fires the autosize event if the height changes.
37376      */
37377     autoSize : function(){
37378         if(!this.grow || !this.textSizeEl){
37379             return;
37380         }
37381         var el = this.el;
37382         var v = el.dom.value;
37383         var ts = this.textSizeEl;
37384
37385         ts.innerHTML = '';
37386         ts.appendChild(document.createTextNode(v));
37387         v = ts.innerHTML;
37388
37389         Roo.fly(ts).setWidth(this.el.getWidth());
37390         if(v.length < 1){
37391             v = "&#160;&#160;";
37392         }else{
37393             if(Roo.isIE){
37394                 v = v.replace(/\n/g, '<p>&#160;</p>');
37395             }
37396             v += "&#160;\n&#160;";
37397         }
37398         ts.innerHTML = v;
37399         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
37400         if(h != this.lastHeight){
37401             this.lastHeight = h;
37402             this.el.setHeight(h);
37403             this.fireEvent("autosize", this, h);
37404         }
37405     }
37406 });/*
37407  * Based on:
37408  * Ext JS Library 1.1.1
37409  * Copyright(c) 2006-2007, Ext JS, LLC.
37410  *
37411  * Originally Released Under LGPL - original licence link has changed is not relivant.
37412  *
37413  * Fork - LGPL
37414  * <script type="text/javascript">
37415  */
37416  
37417
37418 /**
37419  * @class Roo.form.NumberField
37420  * @extends Roo.form.TextField
37421  * Numeric text field that provides automatic keystroke filtering and numeric validation.
37422  * @constructor
37423  * Creates a new NumberField
37424  * @param {Object} config Configuration options
37425  */
37426 Roo.form.NumberField = function(config){
37427     Roo.form.NumberField.superclass.constructor.call(this, config);
37428 };
37429
37430 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
37431     /**
37432      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
37433      */
37434     fieldClass: "x-form-field x-form-num-field",
37435     /**
37436      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
37437      */
37438     allowDecimals : true,
37439     /**
37440      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
37441      */
37442     decimalSeparator : ".",
37443     /**
37444      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
37445      */
37446     decimalPrecision : 2,
37447     /**
37448      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
37449      */
37450     allowNegative : true,
37451     /**
37452      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
37453      */
37454     minValue : Number.NEGATIVE_INFINITY,
37455     /**
37456      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
37457      */
37458     maxValue : Number.MAX_VALUE,
37459     /**
37460      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37461      */
37462     minText : "The minimum value for this field is {0}",
37463     /**
37464      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37465      */
37466     maxText : "The maximum value for this field is {0}",
37467     /**
37468      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37469      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37470      */
37471     nanText : "{0} is not a valid number",
37472
37473     // private
37474     initEvents : function(){
37475         Roo.form.NumberField.superclass.initEvents.call(this);
37476         var allowed = "0123456789";
37477         if(this.allowDecimals){
37478             allowed += this.decimalSeparator;
37479         }
37480         if(this.allowNegative){
37481             allowed += "-";
37482         }
37483         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37484         var keyPress = function(e){
37485             var k = e.getKey();
37486             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37487                 return;
37488             }
37489             var c = e.getCharCode();
37490             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37491                 e.stopEvent();
37492             }
37493         };
37494         this.el.on("keypress", keyPress, this);
37495     },
37496
37497     // private
37498     validateValue : function(value){
37499         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
37500             return false;
37501         }
37502         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37503              return true;
37504         }
37505         var num = this.parseValue(value);
37506         if(isNaN(num)){
37507             this.markInvalid(String.format(this.nanText, value));
37508             return false;
37509         }
37510         if(num < this.minValue){
37511             this.markInvalid(String.format(this.minText, this.minValue));
37512             return false;
37513         }
37514         if(num > this.maxValue){
37515             this.markInvalid(String.format(this.maxText, this.maxValue));
37516             return false;
37517         }
37518         return true;
37519     },
37520
37521     getValue : function(){
37522         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
37523     },
37524
37525     // private
37526     parseValue : function(value){
37527         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37528         return isNaN(value) ? '' : value;
37529     },
37530
37531     // private
37532     fixPrecision : function(value){
37533         var nan = isNaN(value);
37534         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37535             return nan ? '' : value;
37536         }
37537         return parseFloat(value).toFixed(this.decimalPrecision);
37538     },
37539
37540     setValue : function(v){
37541         v = this.fixPrecision(v);
37542         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
37543     },
37544
37545     // private
37546     decimalPrecisionFcn : function(v){
37547         return Math.floor(v);
37548     },
37549
37550     beforeBlur : function(){
37551         var v = this.parseValue(this.getRawValue());
37552         if(v){
37553             this.setValue(v);
37554         }
37555     }
37556 });/*
37557  * Based on:
37558  * Ext JS Library 1.1.1
37559  * Copyright(c) 2006-2007, Ext JS, LLC.
37560  *
37561  * Originally Released Under LGPL - original licence link has changed is not relivant.
37562  *
37563  * Fork - LGPL
37564  * <script type="text/javascript">
37565  */
37566  
37567 /**
37568  * @class Roo.form.DateField
37569  * @extends Roo.form.TriggerField
37570  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37571 * @constructor
37572 * Create a new DateField
37573 * @param {Object} config
37574  */
37575 Roo.form.DateField = function(config){
37576     Roo.form.DateField.superclass.constructor.call(this, config);
37577     
37578       this.addEvents({
37579          
37580         /**
37581          * @event select
37582          * Fires when a date is selected
37583              * @param {Roo.form.DateField} combo This combo box
37584              * @param {Date} date The date selected
37585              */
37586         'select' : true
37587          
37588     });
37589     
37590     
37591     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37592     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37593     this.ddMatch = null;
37594     if(this.disabledDates){
37595         var dd = this.disabledDates;
37596         var re = "(?:";
37597         for(var i = 0; i < dd.length; i++){
37598             re += dd[i];
37599             if(i != dd.length-1) re += "|";
37600         }
37601         this.ddMatch = new RegExp(re + ")");
37602     }
37603 };
37604
37605 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
37606     /**
37607      * @cfg {String} format
37608      * The default date format string which can be overriden for localization support.  The format must be
37609      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37610      */
37611     format : "m/d/y",
37612     /**
37613      * @cfg {String} altFormats
37614      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37615      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37616      */
37617     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37618     /**
37619      * @cfg {Array} disabledDays
37620      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37621      */
37622     disabledDays : null,
37623     /**
37624      * @cfg {String} disabledDaysText
37625      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37626      */
37627     disabledDaysText : "Disabled",
37628     /**
37629      * @cfg {Array} disabledDates
37630      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37631      * expression so they are very powerful. Some examples:
37632      * <ul>
37633      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37634      * <li>["03/08", "09/16"] would disable those days for every year</li>
37635      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37636      * <li>["03/../2006"] would disable every day in March 2006</li>
37637      * <li>["^03"] would disable every day in every March</li>
37638      * </ul>
37639      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37640      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37641      */
37642     disabledDates : null,
37643     /**
37644      * @cfg {String} disabledDatesText
37645      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37646      */
37647     disabledDatesText : "Disabled",
37648     /**
37649      * @cfg {Date/String} minValue
37650      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37651      * valid format (defaults to null).
37652      */
37653     minValue : null,
37654     /**
37655      * @cfg {Date/String} maxValue
37656      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37657      * valid format (defaults to null).
37658      */
37659     maxValue : null,
37660     /**
37661      * @cfg {String} minText
37662      * The error text to display when the date in the cell is before minValue (defaults to
37663      * 'The date in this field must be after {minValue}').
37664      */
37665     minText : "The date in this field must be equal to or after {0}",
37666     /**
37667      * @cfg {String} maxText
37668      * The error text to display when the date in the cell is after maxValue (defaults to
37669      * 'The date in this field must be before {maxValue}').
37670      */
37671     maxText : "The date in this field must be equal to or before {0}",
37672     /**
37673      * @cfg {String} invalidText
37674      * The error text to display when the date in the field is invalid (defaults to
37675      * '{value} is not a valid date - it must be in the format {format}').
37676      */
37677     invalidText : "{0} is not a valid date - it must be in the format {1}",
37678     /**
37679      * @cfg {String} triggerClass
37680      * An additional CSS class used to style the trigger button.  The trigger will always get the
37681      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37682      * which displays a calendar icon).
37683      */
37684     triggerClass : 'x-form-date-trigger',
37685     
37686
37687     /**
37688      * @cfg {Boolean} useIso
37689      * if enabled, then the date field will use a hidden field to store the 
37690      * real value as iso formated date. default (false)
37691      */ 
37692     useIso : false,
37693     /**
37694      * @cfg {String/Object} autoCreate
37695      * A DomHelper element spec, or true for a default element spec (defaults to
37696      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37697      */ 
37698     // private
37699     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37700     
37701     // private
37702     hiddenField: false,
37703     
37704     onRender : function(ct, position)
37705     {
37706         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37707         if (this.useIso) {
37708             this.el.dom.removeAttribute('name'); 
37709             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37710                     'before', true);
37711             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
37712             // prevent input submission
37713             this.hiddenName = this.name;
37714         }
37715             
37716             
37717     },
37718     
37719     // private
37720     validateValue : function(value)
37721     {
37722         value = this.formatDate(value);
37723         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37724             Roo.log('super failed');
37725             return false;
37726         }
37727         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37728              return true;
37729         }
37730         var svalue = value;
37731         value = this.parseDate(value);
37732         if(!value){
37733             Roo.log('parse date failed' + svalue);
37734             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37735             return false;
37736         }
37737         var time = value.getTime();
37738         if(this.minValue && time < this.minValue.getTime()){
37739             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37740             return false;
37741         }
37742         if(this.maxValue && time > this.maxValue.getTime()){
37743             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37744             return false;
37745         }
37746         if(this.disabledDays){
37747             var day = value.getDay();
37748             for(var i = 0; i < this.disabledDays.length; i++) {
37749                 if(day === this.disabledDays[i]){
37750                     this.markInvalid(this.disabledDaysText);
37751                     return false;
37752                 }
37753             }
37754         }
37755         var fvalue = this.formatDate(value);
37756         if(this.ddMatch && this.ddMatch.test(fvalue)){
37757             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37758             return false;
37759         }
37760         return true;
37761     },
37762
37763     // private
37764     // Provides logic to override the default TriggerField.validateBlur which just returns true
37765     validateBlur : function(){
37766         return !this.menu || !this.menu.isVisible();
37767     },
37768
37769     /**
37770      * Returns the current date value of the date field.
37771      * @return {Date} The date value
37772      */
37773     getValue : function(){
37774         
37775         return  this.hiddenField ?
37776                 this.hiddenField.value :
37777                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37778     },
37779
37780     /**
37781      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37782      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37783      * (the default format used is "m/d/y").
37784      * <br />Usage:
37785      * <pre><code>
37786 //All of these calls set the same date value (May 4, 2006)
37787
37788 //Pass a date object:
37789 var dt = new Date('5/4/06');
37790 dateField.setValue(dt);
37791
37792 //Pass a date string (default format):
37793 dateField.setValue('5/4/06');
37794
37795 //Pass a date string (custom format):
37796 dateField.format = 'Y-m-d';
37797 dateField.setValue('2006-5-4');
37798 </code></pre>
37799      * @param {String/Date} date The date or valid date string
37800      */
37801     setValue : function(date){
37802         if (this.hiddenField) {
37803             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37804         }
37805         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37806         // make sure the value field is always stored as a date..
37807         this.value = this.parseDate(date);
37808         
37809         
37810     },
37811
37812     // private
37813     parseDate : function(value){
37814         if(!value || value instanceof Date){
37815             return value;
37816         }
37817         var v = Date.parseDate(value, this.format);
37818          if (!v && this.useIso) {
37819             v = Date.parseDate(value, 'Y-m-d');
37820         }
37821         if(!v && this.altFormats){
37822             if(!this.altFormatsArray){
37823                 this.altFormatsArray = this.altFormats.split("|");
37824             }
37825             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37826                 v = Date.parseDate(value, this.altFormatsArray[i]);
37827             }
37828         }
37829         return v;
37830     },
37831
37832     // private
37833     formatDate : function(date, fmt){
37834         return (!date || !(date instanceof Date)) ?
37835                date : date.dateFormat(fmt || this.format);
37836     },
37837
37838     // private
37839     menuListeners : {
37840         select: function(m, d){
37841             
37842             this.setValue(d);
37843             this.fireEvent('select', this, d);
37844         },
37845         show : function(){ // retain focus styling
37846             this.onFocus();
37847         },
37848         hide : function(){
37849             this.focus.defer(10, this);
37850             var ml = this.menuListeners;
37851             this.menu.un("select", ml.select,  this);
37852             this.menu.un("show", ml.show,  this);
37853             this.menu.un("hide", ml.hide,  this);
37854         }
37855     },
37856
37857     // private
37858     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37859     onTriggerClick : function(){
37860         if(this.disabled){
37861             return;
37862         }
37863         if(this.menu == null){
37864             this.menu = new Roo.menu.DateMenu();
37865         }
37866         Roo.apply(this.menu.picker,  {
37867             showClear: this.allowBlank,
37868             minDate : this.minValue,
37869             maxDate : this.maxValue,
37870             disabledDatesRE : this.ddMatch,
37871             disabledDatesText : this.disabledDatesText,
37872             disabledDays : this.disabledDays,
37873             disabledDaysText : this.disabledDaysText,
37874             format : this.useIso ? 'Y-m-d' : this.format,
37875             minText : String.format(this.minText, this.formatDate(this.minValue)),
37876             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37877         });
37878         this.menu.on(Roo.apply({}, this.menuListeners, {
37879             scope:this
37880         }));
37881         this.menu.picker.setValue(this.getValue() || new Date());
37882         this.menu.show(this.el, "tl-bl?");
37883     },
37884
37885     beforeBlur : function(){
37886         var v = this.parseDate(this.getRawValue());
37887         if(v){
37888             this.setValue(v);
37889         }
37890     }
37891
37892     /** @cfg {Boolean} grow @hide */
37893     /** @cfg {Number} growMin @hide */
37894     /** @cfg {Number} growMax @hide */
37895     /**
37896      * @hide
37897      * @method autoSize
37898      */
37899 });/*
37900  * Based on:
37901  * Ext JS Library 1.1.1
37902  * Copyright(c) 2006-2007, Ext JS, LLC.
37903  *
37904  * Originally Released Under LGPL - original licence link has changed is not relivant.
37905  *
37906  * Fork - LGPL
37907  * <script type="text/javascript">
37908  */
37909  
37910 /**
37911  * @class Roo.form.MonthField
37912  * @extends Roo.form.TriggerField
37913  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37914 * @constructor
37915 * Create a new MonthField
37916 * @param {Object} config
37917  */
37918 Roo.form.MonthField = function(config){
37919     
37920     Roo.form.MonthField.superclass.constructor.call(this, config);
37921     
37922       this.addEvents({
37923          
37924         /**
37925          * @event select
37926          * Fires when a date is selected
37927              * @param {Roo.form.MonthFieeld} combo This combo box
37928              * @param {Date} date The date selected
37929              */
37930         'select' : true
37931          
37932     });
37933     
37934     
37935     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37936     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37937     this.ddMatch = null;
37938     if(this.disabledDates){
37939         var dd = this.disabledDates;
37940         var re = "(?:";
37941         for(var i = 0; i < dd.length; i++){
37942             re += dd[i];
37943             if(i != dd.length-1) re += "|";
37944         }
37945         this.ddMatch = new RegExp(re + ")");
37946     }
37947 };
37948
37949 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
37950     /**
37951      * @cfg {String} format
37952      * The default date format string which can be overriden for localization support.  The format must be
37953      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37954      */
37955     format : "M Y",
37956     /**
37957      * @cfg {String} altFormats
37958      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37959      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37960      */
37961     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
37962     /**
37963      * @cfg {Array} disabledDays
37964      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37965      */
37966     disabledDays : [0,1,2,3,4,5,6],
37967     /**
37968      * @cfg {String} disabledDaysText
37969      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37970      */
37971     disabledDaysText : "Disabled",
37972     /**
37973      * @cfg {Array} disabledDates
37974      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37975      * expression so they are very powerful. Some examples:
37976      * <ul>
37977      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37978      * <li>["03/08", "09/16"] would disable those days for every year</li>
37979      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37980      * <li>["03/../2006"] would disable every day in March 2006</li>
37981      * <li>["^03"] would disable every day in every March</li>
37982      * </ul>
37983      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37984      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37985      */
37986     disabledDates : null,
37987     /**
37988      * @cfg {String} disabledDatesText
37989      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37990      */
37991     disabledDatesText : "Disabled",
37992     /**
37993      * @cfg {Date/String} minValue
37994      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37995      * valid format (defaults to null).
37996      */
37997     minValue : null,
37998     /**
37999      * @cfg {Date/String} maxValue
38000      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38001      * valid format (defaults to null).
38002      */
38003     maxValue : null,
38004     /**
38005      * @cfg {String} minText
38006      * The error text to display when the date in the cell is before minValue (defaults to
38007      * 'The date in this field must be after {minValue}').
38008      */
38009     minText : "The date in this field must be equal to or after {0}",
38010     /**
38011      * @cfg {String} maxTextf
38012      * The error text to display when the date in the cell is after maxValue (defaults to
38013      * 'The date in this field must be before {maxValue}').
38014      */
38015     maxText : "The date in this field must be equal to or before {0}",
38016     /**
38017      * @cfg {String} invalidText
38018      * The error text to display when the date in the field is invalid (defaults to
38019      * '{value} is not a valid date - it must be in the format {format}').
38020      */
38021     invalidText : "{0} is not a valid date - it must be in the format {1}",
38022     /**
38023      * @cfg {String} triggerClass
38024      * An additional CSS class used to style the trigger button.  The trigger will always get the
38025      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38026      * which displays a calendar icon).
38027      */
38028     triggerClass : 'x-form-date-trigger',
38029     
38030
38031     /**
38032      * @cfg {Boolean} useIso
38033      * if enabled, then the date field will use a hidden field to store the 
38034      * real value as iso formated date. default (true)
38035      */ 
38036     useIso : true,
38037     /**
38038      * @cfg {String/Object} autoCreate
38039      * A DomHelper element spec, or true for a default element spec (defaults to
38040      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38041      */ 
38042     // private
38043     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38044     
38045     // private
38046     hiddenField: false,
38047     
38048     hideMonthPicker : false,
38049     
38050     onRender : function(ct, position)
38051     {
38052         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38053         if (this.useIso) {
38054             this.el.dom.removeAttribute('name'); 
38055             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38056                     'before', true);
38057             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38058             // prevent input submission
38059             this.hiddenName = this.name;
38060         }
38061             
38062             
38063     },
38064     
38065     // private
38066     validateValue : function(value)
38067     {
38068         value = this.formatDate(value);
38069         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38070             return false;
38071         }
38072         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38073              return true;
38074         }
38075         var svalue = value;
38076         value = this.parseDate(value);
38077         if(!value){
38078             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38079             return false;
38080         }
38081         var time = value.getTime();
38082         if(this.minValue && time < this.minValue.getTime()){
38083             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38084             return false;
38085         }
38086         if(this.maxValue && time > this.maxValue.getTime()){
38087             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38088             return false;
38089         }
38090         /*if(this.disabledDays){
38091             var day = value.getDay();
38092             for(var i = 0; i < this.disabledDays.length; i++) {
38093                 if(day === this.disabledDays[i]){
38094                     this.markInvalid(this.disabledDaysText);
38095                     return false;
38096                 }
38097             }
38098         }
38099         */
38100         var fvalue = this.formatDate(value);
38101         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38102             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38103             return false;
38104         }
38105         */
38106         return true;
38107     },
38108
38109     // private
38110     // Provides logic to override the default TriggerField.validateBlur which just returns true
38111     validateBlur : function(){
38112         return !this.menu || !this.menu.isVisible();
38113     },
38114
38115     /**
38116      * Returns the current date value of the date field.
38117      * @return {Date} The date value
38118      */
38119     getValue : function(){
38120         
38121         
38122         
38123         return  this.hiddenField ?
38124                 this.hiddenField.value :
38125                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38126     },
38127
38128     /**
38129      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38130      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38131      * (the default format used is "m/d/y").
38132      * <br />Usage:
38133      * <pre><code>
38134 //All of these calls set the same date value (May 4, 2006)
38135
38136 //Pass a date object:
38137 var dt = new Date('5/4/06');
38138 monthField.setValue(dt);
38139
38140 //Pass a date string (default format):
38141 monthField.setValue('5/4/06');
38142
38143 //Pass a date string (custom format):
38144 monthField.format = 'Y-m-d';
38145 monthField.setValue('2006-5-4');
38146 </code></pre>
38147      * @param {String/Date} date The date or valid date string
38148      */
38149     setValue : function(date){
38150         Roo.log('month setValue' + date);
38151         // can only be first of month..
38152         
38153         var val = this.parseDate(date);
38154         
38155         if (this.hiddenField) {
38156             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38157         }
38158         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38159         this.value = this.parseDate(date);
38160     },
38161
38162     // private
38163     parseDate : function(value){
38164         if(!value || value instanceof Date){
38165             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38166             return value;
38167         }
38168         var v = Date.parseDate(value, this.format);
38169         if (!v && this.useIso) {
38170             v = Date.parseDate(value, 'Y-m-d');
38171         }
38172         if (v) {
38173             // 
38174             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38175         }
38176         
38177         
38178         if(!v && this.altFormats){
38179             if(!this.altFormatsArray){
38180                 this.altFormatsArray = this.altFormats.split("|");
38181             }
38182             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38183                 v = Date.parseDate(value, this.altFormatsArray[i]);
38184             }
38185         }
38186         return v;
38187     },
38188
38189     // private
38190     formatDate : function(date, fmt){
38191         return (!date || !(date instanceof Date)) ?
38192                date : date.dateFormat(fmt || this.format);
38193     },
38194
38195     // private
38196     menuListeners : {
38197         select: function(m, d){
38198             this.setValue(d);
38199             this.fireEvent('select', this, d);
38200         },
38201         show : function(){ // retain focus styling
38202             this.onFocus();
38203         },
38204         hide : function(){
38205             this.focus.defer(10, this);
38206             var ml = this.menuListeners;
38207             this.menu.un("select", ml.select,  this);
38208             this.menu.un("show", ml.show,  this);
38209             this.menu.un("hide", ml.hide,  this);
38210         }
38211     },
38212     // private
38213     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38214     onTriggerClick : function(){
38215         if(this.disabled){
38216             return;
38217         }
38218         if(this.menu == null){
38219             this.menu = new Roo.menu.DateMenu();
38220            
38221         }
38222         
38223         Roo.apply(this.menu.picker,  {
38224             
38225             showClear: this.allowBlank,
38226             minDate : this.minValue,
38227             maxDate : this.maxValue,
38228             disabledDatesRE : this.ddMatch,
38229             disabledDatesText : this.disabledDatesText,
38230             
38231             format : this.useIso ? 'Y-m-d' : this.format,
38232             minText : String.format(this.minText, this.formatDate(this.minValue)),
38233             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38234             
38235         });
38236          this.menu.on(Roo.apply({}, this.menuListeners, {
38237             scope:this
38238         }));
38239        
38240         
38241         var m = this.menu;
38242         var p = m.picker;
38243         
38244         // hide month picker get's called when we called by 'before hide';
38245         
38246         var ignorehide = true;
38247         p.hideMonthPicker  = function(disableAnim){
38248             if (ignorehide) {
38249                 return;
38250             }
38251              if(this.monthPicker){
38252                 Roo.log("hideMonthPicker called");
38253                 if(disableAnim === true){
38254                     this.monthPicker.hide();
38255                 }else{
38256                     this.monthPicker.slideOut('t', {duration:.2});
38257                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38258                     p.fireEvent("select", this, this.value);
38259                     m.hide();
38260                 }
38261             }
38262         }
38263         
38264         Roo.log('picker set value');
38265         Roo.log(this.getValue());
38266         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38267         m.show(this.el, 'tl-bl?');
38268         ignorehide  = false;
38269         // this will trigger hideMonthPicker..
38270         
38271         
38272         // hidden the day picker
38273         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38274         
38275         
38276         
38277       
38278         
38279         p.showMonthPicker.defer(100, p);
38280     
38281         
38282        
38283     },
38284
38285     beforeBlur : function(){
38286         var v = this.parseDate(this.getRawValue());
38287         if(v){
38288             this.setValue(v);
38289         }
38290     }
38291
38292     /** @cfg {Boolean} grow @hide */
38293     /** @cfg {Number} growMin @hide */
38294     /** @cfg {Number} growMax @hide */
38295     /**
38296      * @hide
38297      * @method autoSize
38298      */
38299 });/*
38300  * Based on:
38301  * Ext JS Library 1.1.1
38302  * Copyright(c) 2006-2007, Ext JS, LLC.
38303  *
38304  * Originally Released Under LGPL - original licence link has changed is not relivant.
38305  *
38306  * Fork - LGPL
38307  * <script type="text/javascript">
38308  */
38309  
38310
38311 /**
38312  * @class Roo.form.ComboBox
38313  * @extends Roo.form.TriggerField
38314  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
38315  * @constructor
38316  * Create a new ComboBox.
38317  * @param {Object} config Configuration options
38318  */
38319 Roo.form.ComboBox = function(config){
38320     Roo.form.ComboBox.superclass.constructor.call(this, config);
38321     this.addEvents({
38322         /**
38323          * @event expand
38324          * Fires when the dropdown list is expanded
38325              * @param {Roo.form.ComboBox} combo This combo box
38326              */
38327         'expand' : true,
38328         /**
38329          * @event collapse
38330          * Fires when the dropdown list is collapsed
38331              * @param {Roo.form.ComboBox} combo This combo box
38332              */
38333         'collapse' : true,
38334         /**
38335          * @event beforeselect
38336          * Fires before a list item is selected. Return false to cancel the selection.
38337              * @param {Roo.form.ComboBox} combo This combo box
38338              * @param {Roo.data.Record} record The data record returned from the underlying store
38339              * @param {Number} index The index of the selected item in the dropdown list
38340              */
38341         'beforeselect' : true,
38342         /**
38343          * @event select
38344          * Fires when a list item is selected
38345              * @param {Roo.form.ComboBox} combo This combo box
38346              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
38347              * @param {Number} index The index of the selected item in the dropdown list
38348              */
38349         'select' : true,
38350         /**
38351          * @event beforequery
38352          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
38353          * The event object passed has these properties:
38354              * @param {Roo.form.ComboBox} combo This combo box
38355              * @param {String} query The query
38356              * @param {Boolean} forceAll true to force "all" query
38357              * @param {Boolean} cancel true to cancel the query
38358              * @param {Object} e The query event object
38359              */
38360         'beforequery': true,
38361          /**
38362          * @event add
38363          * Fires when the 'add' icon is pressed (add a listener to enable add button)
38364              * @param {Roo.form.ComboBox} combo This combo box
38365              */
38366         'add' : true,
38367         /**
38368          * @event edit
38369          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
38370              * @param {Roo.form.ComboBox} combo This combo box
38371              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
38372              */
38373         'edit' : true
38374         
38375         
38376     });
38377     if(this.transform){
38378         this.allowDomMove = false;
38379         var s = Roo.getDom(this.transform);
38380         if(!this.hiddenName){
38381             this.hiddenName = s.name;
38382         }
38383         if(!this.store){
38384             this.mode = 'local';
38385             var d = [], opts = s.options;
38386             for(var i = 0, len = opts.length;i < len; i++){
38387                 var o = opts[i];
38388                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
38389                 if(o.selected) {
38390                     this.value = value;
38391                 }
38392                 d.push([value, o.text]);
38393             }
38394             this.store = new Roo.data.SimpleStore({
38395                 'id': 0,
38396                 fields: ['value', 'text'],
38397                 data : d
38398             });
38399             this.valueField = 'value';
38400             this.displayField = 'text';
38401         }
38402         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
38403         if(!this.lazyRender){
38404             this.target = true;
38405             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
38406             s.parentNode.removeChild(s); // remove it
38407             this.render(this.el.parentNode);
38408         }else{
38409             s.parentNode.removeChild(s); // remove it
38410         }
38411
38412     }
38413     if (this.store) {
38414         this.store = Roo.factory(this.store, Roo.data);
38415     }
38416     
38417     this.selectedIndex = -1;
38418     if(this.mode == 'local'){
38419         if(config.queryDelay === undefined){
38420             this.queryDelay = 10;
38421         }
38422         if(config.minChars === undefined){
38423             this.minChars = 0;
38424         }
38425     }
38426 };
38427
38428 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
38429     /**
38430      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
38431      */
38432     /**
38433      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
38434      * rendering into an Roo.Editor, defaults to false)
38435      */
38436     /**
38437      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
38438      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
38439      */
38440     /**
38441      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
38442      */
38443     /**
38444      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
38445      * the dropdown list (defaults to undefined, with no header element)
38446      */
38447
38448      /**
38449      * @cfg {String/Roo.Template} tpl The template to use to render the output
38450      */
38451      
38452     // private
38453     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
38454     /**
38455      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
38456      */
38457     listWidth: undefined,
38458     /**
38459      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
38460      * mode = 'remote' or 'text' if mode = 'local')
38461      */
38462     displayField: undefined,
38463     /**
38464      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
38465      * mode = 'remote' or 'value' if mode = 'local'). 
38466      * Note: use of a valueField requires the user make a selection
38467      * in order for a value to be mapped.
38468      */
38469     valueField: undefined,
38470     
38471     
38472     /**
38473      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
38474      * field's data value (defaults to the underlying DOM element's name)
38475      */
38476     hiddenName: undefined,
38477     /**
38478      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
38479      */
38480     listClass: '',
38481     /**
38482      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
38483      */
38484     selectedClass: 'x-combo-selected',
38485     /**
38486      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38487      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
38488      * which displays a downward arrow icon).
38489      */
38490     triggerClass : 'x-form-arrow-trigger',
38491     /**
38492      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
38493      */
38494     shadow:'sides',
38495     /**
38496      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
38497      * anchor positions (defaults to 'tl-bl')
38498      */
38499     listAlign: 'tl-bl?',
38500     /**
38501      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
38502      */
38503     maxHeight: 300,
38504     /**
38505      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
38506      * query specified by the allQuery config option (defaults to 'query')
38507      */
38508     triggerAction: 'query',
38509     /**
38510      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
38511      * (defaults to 4, does not apply if editable = false)
38512      */
38513     minChars : 4,
38514     /**
38515      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
38516      * delay (typeAheadDelay) if it matches a known value (defaults to false)
38517      */
38518     typeAhead: false,
38519     /**
38520      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
38521      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
38522      */
38523     queryDelay: 500,
38524     /**
38525      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
38526      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
38527      */
38528     pageSize: 0,
38529     /**
38530      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
38531      * when editable = true (defaults to false)
38532      */
38533     selectOnFocus:false,
38534     /**
38535      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
38536      */
38537     queryParam: 'query',
38538     /**
38539      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
38540      * when mode = 'remote' (defaults to 'Loading...')
38541      */
38542     loadingText: 'Loading...',
38543     /**
38544      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
38545      */
38546     resizable: false,
38547     /**
38548      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
38549      */
38550     handleHeight : 8,
38551     /**
38552      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
38553      * traditional select (defaults to true)
38554      */
38555     editable: true,
38556     /**
38557      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
38558      */
38559     allQuery: '',
38560     /**
38561      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
38562      */
38563     mode: 'remote',
38564     /**
38565      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
38566      * listWidth has a higher value)
38567      */
38568     minListWidth : 70,
38569     /**
38570      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
38571      * allow the user to set arbitrary text into the field (defaults to false)
38572      */
38573     forceSelection:false,
38574     /**
38575      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
38576      * if typeAhead = true (defaults to 250)
38577      */
38578     typeAheadDelay : 250,
38579     /**
38580      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
38581      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
38582      */
38583     valueNotFoundText : undefined,
38584     /**
38585      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
38586      */
38587     blockFocus : false,
38588     
38589     /**
38590      * @cfg {Boolean} disableClear Disable showing of clear button.
38591      */
38592     disableClear : false,
38593     /**
38594      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
38595      */
38596     alwaysQuery : false,
38597     
38598     //private
38599     addicon : false,
38600     editicon: false,
38601     
38602     // element that contains real text value.. (when hidden is used..)
38603      
38604     // private
38605     onRender : function(ct, position){
38606         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
38607         if(this.hiddenName){
38608             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
38609                     'before', true);
38610             this.hiddenField.value =
38611                 this.hiddenValue !== undefined ? this.hiddenValue :
38612                 this.value !== undefined ? this.value : '';
38613
38614             // prevent input submission
38615             this.el.dom.removeAttribute('name');
38616              
38617              
38618         }
38619         if(Roo.isGecko){
38620             this.el.dom.setAttribute('autocomplete', 'off');
38621         }
38622
38623         var cls = 'x-combo-list';
38624
38625         this.list = new Roo.Layer({
38626             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
38627         });
38628
38629         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
38630         this.list.setWidth(lw);
38631         this.list.swallowEvent('mousewheel');
38632         this.assetHeight = 0;
38633
38634         if(this.title){
38635             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
38636             this.assetHeight += this.header.getHeight();
38637         }
38638
38639         this.innerList = this.list.createChild({cls:cls+'-inner'});
38640         this.innerList.on('mouseover', this.onViewOver, this);
38641         this.innerList.on('mousemove', this.onViewMove, this);
38642         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38643         
38644         if(this.allowBlank && !this.pageSize && !this.disableClear){
38645             this.footer = this.list.createChild({cls:cls+'-ft'});
38646             this.pageTb = new Roo.Toolbar(this.footer);
38647            
38648         }
38649         if(this.pageSize){
38650             this.footer = this.list.createChild({cls:cls+'-ft'});
38651             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
38652                     {pageSize: this.pageSize});
38653             
38654         }
38655         
38656         if (this.pageTb && this.allowBlank && !this.disableClear) {
38657             var _this = this;
38658             this.pageTb.add(new Roo.Toolbar.Fill(), {
38659                 cls: 'x-btn-icon x-btn-clear',
38660                 text: '&#160;',
38661                 handler: function()
38662                 {
38663                     _this.collapse();
38664                     _this.clearValue();
38665                     _this.onSelect(false, -1);
38666                 }
38667             });
38668         }
38669         if (this.footer) {
38670             this.assetHeight += this.footer.getHeight();
38671         }
38672         
38673
38674         if(!this.tpl){
38675             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
38676         }
38677
38678         this.view = new Roo.View(this.innerList, this.tpl, {
38679             singleSelect:true, store: this.store, selectedClass: this.selectedClass
38680         });
38681
38682         this.view.on('click', this.onViewClick, this);
38683
38684         this.store.on('beforeload', this.onBeforeLoad, this);
38685         this.store.on('load', this.onLoad, this);
38686         this.store.on('loadexception', this.onLoadException, this);
38687
38688         if(this.resizable){
38689             this.resizer = new Roo.Resizable(this.list,  {
38690                pinned:true, handles:'se'
38691             });
38692             this.resizer.on('resize', function(r, w, h){
38693                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
38694                 this.listWidth = w;
38695                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
38696                 this.restrictHeight();
38697             }, this);
38698             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
38699         }
38700         if(!this.editable){
38701             this.editable = true;
38702             this.setEditable(false);
38703         }  
38704         
38705         
38706         if (typeof(this.events.add.listeners) != 'undefined') {
38707             
38708             this.addicon = this.wrap.createChild(
38709                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
38710        
38711             this.addicon.on('click', function(e) {
38712                 this.fireEvent('add', this);
38713             }, this);
38714         }
38715         if (typeof(this.events.edit.listeners) != 'undefined') {
38716             
38717             this.editicon = this.wrap.createChild(
38718                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
38719             if (this.addicon) {
38720                 this.editicon.setStyle('margin-left', '40px');
38721             }
38722             this.editicon.on('click', function(e) {
38723                 
38724                 // we fire even  if inothing is selected..
38725                 this.fireEvent('edit', this, this.lastData );
38726                 
38727             }, this);
38728         }
38729         
38730         
38731         
38732     },
38733
38734     // private
38735     initEvents : function(){
38736         Roo.form.ComboBox.superclass.initEvents.call(this);
38737
38738         this.keyNav = new Roo.KeyNav(this.el, {
38739             "up" : function(e){
38740                 this.inKeyMode = true;
38741                 this.selectPrev();
38742             },
38743
38744             "down" : function(e){
38745                 if(!this.isExpanded()){
38746                     this.onTriggerClick();
38747                 }else{
38748                     this.inKeyMode = true;
38749                     this.selectNext();
38750                 }
38751             },
38752
38753             "enter" : function(e){
38754                 this.onViewClick();
38755                 //return true;
38756             },
38757
38758             "esc" : function(e){
38759                 this.collapse();
38760             },
38761
38762             "tab" : function(e){
38763                 this.onViewClick(false);
38764                 this.fireEvent("specialkey", this, e);
38765                 return true;
38766             },
38767
38768             scope : this,
38769
38770             doRelay : function(foo, bar, hname){
38771                 if(hname == 'down' || this.scope.isExpanded()){
38772                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38773                 }
38774                 return true;
38775             },
38776
38777             forceKeyDown: true
38778         });
38779         this.queryDelay = Math.max(this.queryDelay || 10,
38780                 this.mode == 'local' ? 10 : 250);
38781         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
38782         if(this.typeAhead){
38783             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
38784         }
38785         if(this.editable !== false){
38786             this.el.on("keyup", this.onKeyUp, this);
38787         }
38788         if(this.forceSelection){
38789             this.on('blur', this.doForce, this);
38790         }
38791     },
38792
38793     onDestroy : function(){
38794         if(this.view){
38795             this.view.setStore(null);
38796             this.view.el.removeAllListeners();
38797             this.view.el.remove();
38798             this.view.purgeListeners();
38799         }
38800         if(this.list){
38801             this.list.destroy();
38802         }
38803         if(this.store){
38804             this.store.un('beforeload', this.onBeforeLoad, this);
38805             this.store.un('load', this.onLoad, this);
38806             this.store.un('loadexception', this.onLoadException, this);
38807         }
38808         Roo.form.ComboBox.superclass.onDestroy.call(this);
38809     },
38810
38811     // private
38812     fireKey : function(e){
38813         if(e.isNavKeyPress() && !this.list.isVisible()){
38814             this.fireEvent("specialkey", this, e);
38815         }
38816     },
38817
38818     // private
38819     onResize: function(w, h){
38820         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
38821         
38822         if(typeof w != 'number'){
38823             // we do not handle it!?!?
38824             return;
38825         }
38826         var tw = this.trigger.getWidth();
38827         tw += this.addicon ? this.addicon.getWidth() : 0;
38828         tw += this.editicon ? this.editicon.getWidth() : 0;
38829         var x = w - tw;
38830         this.el.setWidth( this.adjustWidth('input', x));
38831             
38832         this.trigger.setStyle('left', x+'px');
38833         
38834         if(this.list && this.listWidth === undefined){
38835             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
38836             this.list.setWidth(lw);
38837             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38838         }
38839         
38840     
38841         
38842     },
38843
38844     /**
38845      * Allow or prevent the user from directly editing the field text.  If false is passed,
38846      * the user will only be able to select from the items defined in the dropdown list.  This method
38847      * is the runtime equivalent of setting the 'editable' config option at config time.
38848      * @param {Boolean} value True to allow the user to directly edit the field text
38849      */
38850     setEditable : function(value){
38851         if(value == this.editable){
38852             return;
38853         }
38854         this.editable = value;
38855         if(!value){
38856             this.el.dom.setAttribute('readOnly', true);
38857             this.el.on('mousedown', this.onTriggerClick,  this);
38858             this.el.addClass('x-combo-noedit');
38859         }else{
38860             this.el.dom.setAttribute('readOnly', false);
38861             this.el.un('mousedown', this.onTriggerClick,  this);
38862             this.el.removeClass('x-combo-noedit');
38863         }
38864     },
38865
38866     // private
38867     onBeforeLoad : function(){
38868         if(!this.hasFocus){
38869             return;
38870         }
38871         this.innerList.update(this.loadingText ?
38872                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
38873         this.restrictHeight();
38874         this.selectedIndex = -1;
38875     },
38876
38877     // private
38878     onLoad : function(){
38879         if(!this.hasFocus){
38880             return;
38881         }
38882         if(this.store.getCount() > 0){
38883             this.expand();
38884             this.restrictHeight();
38885             if(this.lastQuery == this.allQuery){
38886                 if(this.editable){
38887                     this.el.dom.select();
38888                 }
38889                 if(!this.selectByValue(this.value, true)){
38890                     this.select(0, true);
38891                 }
38892             }else{
38893                 this.selectNext();
38894                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
38895                     this.taTask.delay(this.typeAheadDelay);
38896                 }
38897             }
38898         }else{
38899             this.onEmptyResults();
38900         }
38901         //this.el.focus();
38902     },
38903     // private
38904     onLoadException : function()
38905     {
38906         this.collapse();
38907         Roo.log(this.store.reader.jsonData);
38908         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38909             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38910         }
38911         
38912         
38913     },
38914     // private
38915     onTypeAhead : function(){
38916         if(this.store.getCount() > 0){
38917             var r = this.store.getAt(0);
38918             var newValue = r.data[this.displayField];
38919             var len = newValue.length;
38920             var selStart = this.getRawValue().length;
38921             if(selStart != len){
38922                 this.setRawValue(newValue);
38923                 this.selectText(selStart, newValue.length);
38924             }
38925         }
38926     },
38927
38928     // private
38929     onSelect : function(record, index){
38930         if(this.fireEvent('beforeselect', this, record, index) !== false){
38931             this.setFromData(index > -1 ? record.data : false);
38932             this.collapse();
38933             this.fireEvent('select', this, record, index);
38934         }
38935     },
38936
38937     /**
38938      * Returns the currently selected field value or empty string if no value is set.
38939      * @return {String} value The selected value
38940      */
38941     getValue : function(){
38942         if(this.valueField){
38943             return typeof this.value != 'undefined' ? this.value : '';
38944         }else{
38945             return Roo.form.ComboBox.superclass.getValue.call(this);
38946         }
38947     },
38948
38949     /**
38950      * Clears any text/value currently set in the field
38951      */
38952     clearValue : function(){
38953         if(this.hiddenField){
38954             this.hiddenField.value = '';
38955         }
38956         this.value = '';
38957         this.setRawValue('');
38958         this.lastSelectionText = '';
38959         this.applyEmptyText();
38960     },
38961
38962     /**
38963      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
38964      * will be displayed in the field.  If the value does not match the data value of an existing item,
38965      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
38966      * Otherwise the field will be blank (although the value will still be set).
38967      * @param {String} value The value to match
38968      */
38969     setValue : function(v){
38970         var text = v;
38971         if(this.valueField){
38972             var r = this.findRecord(this.valueField, v);
38973             if(r){
38974                 text = r.data[this.displayField];
38975             }else if(this.valueNotFoundText !== undefined){
38976                 text = this.valueNotFoundText;
38977             }
38978         }
38979         this.lastSelectionText = text;
38980         if(this.hiddenField){
38981             this.hiddenField.value = v;
38982         }
38983         Roo.form.ComboBox.superclass.setValue.call(this, text);
38984         this.value = v;
38985     },
38986     /**
38987      * @property {Object} the last set data for the element
38988      */
38989     
38990     lastData : false,
38991     /**
38992      * Sets the value of the field based on a object which is related to the record format for the store.
38993      * @param {Object} value the value to set as. or false on reset?
38994      */
38995     setFromData : function(o){
38996         var dv = ''; // display value
38997         var vv = ''; // value value..
38998         this.lastData = o;
38999         if (this.displayField) {
39000             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39001         } else {
39002             // this is an error condition!!!
39003             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39004         }
39005         
39006         if(this.valueField){
39007             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39008         }
39009         if(this.hiddenField){
39010             this.hiddenField.value = vv;
39011             
39012             this.lastSelectionText = dv;
39013             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39014             this.value = vv;
39015             return;
39016         }
39017         // no hidden field.. - we store the value in 'value', but still display
39018         // display field!!!!
39019         this.lastSelectionText = dv;
39020         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39021         this.value = vv;
39022         
39023         
39024     },
39025     // private
39026     reset : function(){
39027         // overridden so that last data is reset..
39028         this.setValue(this.originalValue);
39029         this.clearInvalid();
39030         this.lastData = false;
39031         if (this.view) {
39032             this.view.clearSelections();
39033         }
39034     },
39035     // private
39036     findRecord : function(prop, value){
39037         var record;
39038         if(this.store.getCount() > 0){
39039             this.store.each(function(r){
39040                 if(r.data[prop] == value){
39041                     record = r;
39042                     return false;
39043                 }
39044                 return true;
39045             });
39046         }
39047         return record;
39048     },
39049     
39050     getName: function()
39051     {
39052         // returns hidden if it's set..
39053         if (!this.rendered) {return ''};
39054         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39055         
39056     },
39057     // private
39058     onViewMove : function(e, t){
39059         this.inKeyMode = false;
39060     },
39061
39062     // private
39063     onViewOver : function(e, t){
39064         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39065             return;
39066         }
39067         var item = this.view.findItemFromChild(t);
39068         if(item){
39069             var index = this.view.indexOf(item);
39070             this.select(index, false);
39071         }
39072     },
39073
39074     // private
39075     onViewClick : function(doFocus)
39076     {
39077         var index = this.view.getSelectedIndexes()[0];
39078         var r = this.store.getAt(index);
39079         if(r){
39080             this.onSelect(r, index);
39081         }
39082         if(doFocus !== false && !this.blockFocus){
39083             this.el.focus();
39084         }
39085     },
39086
39087     // private
39088     restrictHeight : function(){
39089         this.innerList.dom.style.height = '';
39090         var inner = this.innerList.dom;
39091         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39092         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39093         this.list.beginUpdate();
39094         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39095         this.list.alignTo(this.el, this.listAlign);
39096         this.list.endUpdate();
39097     },
39098
39099     // private
39100     onEmptyResults : function(){
39101         this.collapse();
39102     },
39103
39104     /**
39105      * Returns true if the dropdown list is expanded, else false.
39106      */
39107     isExpanded : function(){
39108         return this.list.isVisible();
39109     },
39110
39111     /**
39112      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39113      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39114      * @param {String} value The data value of the item to select
39115      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39116      * selected item if it is not currently in view (defaults to true)
39117      * @return {Boolean} True if the value matched an item in the list, else false
39118      */
39119     selectByValue : function(v, scrollIntoView){
39120         if(v !== undefined && v !== null){
39121             var r = this.findRecord(this.valueField || this.displayField, v);
39122             if(r){
39123                 this.select(this.store.indexOf(r), scrollIntoView);
39124                 return true;
39125             }
39126         }
39127         return false;
39128     },
39129
39130     /**
39131      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39132      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39133      * @param {Number} index The zero-based index of the list item to select
39134      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39135      * selected item if it is not currently in view (defaults to true)
39136      */
39137     select : function(index, scrollIntoView){
39138         this.selectedIndex = index;
39139         this.view.select(index);
39140         if(scrollIntoView !== false){
39141             var el = this.view.getNode(index);
39142             if(el){
39143                 this.innerList.scrollChildIntoView(el, false);
39144             }
39145         }
39146     },
39147
39148     // private
39149     selectNext : function(){
39150         var ct = this.store.getCount();
39151         if(ct > 0){
39152             if(this.selectedIndex == -1){
39153                 this.select(0);
39154             }else if(this.selectedIndex < ct-1){
39155                 this.select(this.selectedIndex+1);
39156             }
39157         }
39158     },
39159
39160     // private
39161     selectPrev : function(){
39162         var ct = this.store.getCount();
39163         if(ct > 0){
39164             if(this.selectedIndex == -1){
39165                 this.select(0);
39166             }else if(this.selectedIndex != 0){
39167                 this.select(this.selectedIndex-1);
39168             }
39169         }
39170     },
39171
39172     // private
39173     onKeyUp : function(e){
39174         if(this.editable !== false && !e.isSpecialKey()){
39175             this.lastKey = e.getKey();
39176             this.dqTask.delay(this.queryDelay);
39177         }
39178     },
39179
39180     // private
39181     validateBlur : function(){
39182         return !this.list || !this.list.isVisible();   
39183     },
39184
39185     // private
39186     initQuery : function(){
39187         this.doQuery(this.getRawValue());
39188     },
39189
39190     // private
39191     doForce : function(){
39192         if(this.el.dom.value.length > 0){
39193             this.el.dom.value =
39194                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39195             this.applyEmptyText();
39196         }
39197     },
39198
39199     /**
39200      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39201      * query allowing the query action to be canceled if needed.
39202      * @param {String} query The SQL query to execute
39203      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39204      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39205      * saved in the current store (defaults to false)
39206      */
39207     doQuery : function(q, forceAll){
39208         if(q === undefined || q === null){
39209             q = '';
39210         }
39211         var qe = {
39212             query: q,
39213             forceAll: forceAll,
39214             combo: this,
39215             cancel:false
39216         };
39217         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39218             return false;
39219         }
39220         q = qe.query;
39221         forceAll = qe.forceAll;
39222         if(forceAll === true || (q.length >= this.minChars)){
39223             if(this.lastQuery != q || this.alwaysQuery){
39224                 this.lastQuery = q;
39225                 if(this.mode == 'local'){
39226                     this.selectedIndex = -1;
39227                     if(forceAll){
39228                         this.store.clearFilter();
39229                     }else{
39230                         this.store.filter(this.displayField, q);
39231                     }
39232                     this.onLoad();
39233                 }else{
39234                     this.store.baseParams[this.queryParam] = q;
39235                     this.store.load({
39236                         params: this.getParams(q)
39237                     });
39238                     this.expand();
39239                 }
39240             }else{
39241                 this.selectedIndex = -1;
39242                 this.onLoad();   
39243             }
39244         }
39245     },
39246
39247     // private
39248     getParams : function(q){
39249         var p = {};
39250         //p[this.queryParam] = q;
39251         if(this.pageSize){
39252             p.start = 0;
39253             p.limit = this.pageSize;
39254         }
39255         return p;
39256     },
39257
39258     /**
39259      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39260      */
39261     collapse : function(){
39262         if(!this.isExpanded()){
39263             return;
39264         }
39265         this.list.hide();
39266         Roo.get(document).un('mousedown', this.collapseIf, this);
39267         Roo.get(document).un('mousewheel', this.collapseIf, this);
39268         if (!this.editable) {
39269             Roo.get(document).un('keydown', this.listKeyPress, this);
39270         }
39271         this.fireEvent('collapse', this);
39272     },
39273
39274     // private
39275     collapseIf : function(e){
39276         if(!e.within(this.wrap) && !e.within(this.list)){
39277             this.collapse();
39278         }
39279     },
39280
39281     /**
39282      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39283      */
39284     expand : function(){
39285         if(this.isExpanded() || !this.hasFocus){
39286             return;
39287         }
39288         this.list.alignTo(this.el, this.listAlign);
39289         this.list.show();
39290         Roo.get(document).on('mousedown', this.collapseIf, this);
39291         Roo.get(document).on('mousewheel', this.collapseIf, this);
39292         if (!this.editable) {
39293             Roo.get(document).on('keydown', this.listKeyPress, this);
39294         }
39295         
39296         this.fireEvent('expand', this);
39297     },
39298
39299     // private
39300     // Implements the default empty TriggerField.onTriggerClick function
39301     onTriggerClick : function(){
39302         if(this.disabled){
39303             return;
39304         }
39305         if(this.isExpanded()){
39306             this.collapse();
39307             if (!this.blockFocus) {
39308                 this.el.focus();
39309             }
39310             
39311         }else {
39312             this.hasFocus = true;
39313             if(this.triggerAction == 'all') {
39314                 this.doQuery(this.allQuery, true);
39315             } else {
39316                 this.doQuery(this.getRawValue());
39317             }
39318             if (!this.blockFocus) {
39319                 this.el.focus();
39320             }
39321         }
39322     },
39323     listKeyPress : function(e)
39324     {
39325         //Roo.log('listkeypress');
39326         // scroll to first matching element based on key pres..
39327         if (e.isSpecialKey()) {
39328             return false;
39329         }
39330         var k = String.fromCharCode(e.getKey()).toUpperCase();
39331         //Roo.log(k);
39332         var match  = false;
39333         var csel = this.view.getSelectedNodes();
39334         var cselitem = false;
39335         if (csel.length) {
39336             var ix = this.view.indexOf(csel[0]);
39337             cselitem  = this.store.getAt(ix);
39338             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
39339                 cselitem = false;
39340             }
39341             
39342         }
39343         
39344         this.store.each(function(v) { 
39345             if (cselitem) {
39346                 // start at existing selection.
39347                 if (cselitem.id == v.id) {
39348                     cselitem = false;
39349                 }
39350                 return;
39351             }
39352                 
39353             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
39354                 match = this.store.indexOf(v);
39355                 return false;
39356             }
39357         }, this);
39358         
39359         if (match === false) {
39360             return true; // no more action?
39361         }
39362         // scroll to?
39363         this.view.select(match);
39364         var sn = Roo.get(this.view.getSelectedNodes()[0])
39365         sn.scrollIntoView(sn.dom.parentNode, false);
39366     }
39367
39368     /** 
39369     * @cfg {Boolean} grow 
39370     * @hide 
39371     */
39372     /** 
39373     * @cfg {Number} growMin 
39374     * @hide 
39375     */
39376     /** 
39377     * @cfg {Number} growMax 
39378     * @hide 
39379     */
39380     /**
39381      * @hide
39382      * @method autoSize
39383      */
39384 });/*
39385  * Copyright(c) 2010-2012, Roo J Solutions Limited
39386  *
39387  * Licence LGPL
39388  *
39389  */
39390
39391 /**
39392  * @class Roo.form.ComboBoxArray
39393  * @extends Roo.form.TextField
39394  * A facebook style adder... for lists of email / people / countries  etc...
39395  * pick multiple items from a combo box, and shows each one.
39396  *
39397  *  Fred [x]  Brian [x]  [Pick another |v]
39398  *
39399  *
39400  *  For this to work: it needs various extra information
39401  *    - normal combo problay has
39402  *      name, hiddenName
39403  *    + displayField, valueField
39404  *
39405  *    For our purpose...
39406  *
39407  *
39408  *   If we change from 'extends' to wrapping...
39409  *   
39410  *  
39411  *
39412  
39413  
39414  * @constructor
39415  * Create a new ComboBoxArray.
39416  * @param {Object} config Configuration options
39417  */
39418  
39419
39420 Roo.form.ComboBoxArray = function(config)
39421 {
39422     
39423     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
39424     
39425     this.items = new Roo.util.MixedCollection(false);
39426     
39427     // construct the child combo...
39428     
39429     
39430     
39431     
39432    
39433     
39434 }
39435
39436  
39437 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
39438
39439     /**
39440      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
39441      */
39442     
39443     lastData : false,
39444     
39445     // behavies liek a hiddne field
39446     inputType:      'hidden',
39447     /**
39448      * @cfg {Number} width The width of the box that displays the selected element
39449      */ 
39450     width:          300,
39451
39452     
39453     
39454     /**
39455      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
39456      */
39457     name : false,
39458     /**
39459      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
39460      */
39461     hiddenName : false,
39462     
39463     
39464     // private the array of items that are displayed..
39465     items  : false,
39466     // private - the hidden field el.
39467     hiddenEl : false,
39468     // private - the filed el..
39469     el : false,
39470     
39471     //validateValue : function() { return true; }, // all values are ok!
39472     //onAddClick: function() { },
39473     
39474     onRender : function(ct, position) 
39475     {
39476         
39477         // create the standard hidden element
39478         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
39479         
39480         
39481         // give fake names to child combo;
39482         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
39483         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
39484         
39485         this.combo = Roo.factory(this.combo, Roo.form);
39486         this.combo.onRender(ct, position);
39487         this.combo.initEvents();
39488         
39489         // assigned so form know we need to do this..
39490         this.store          = this.combo.store;
39491         this.valueField     = this.combo.valueField;
39492         this.displayField   = this.combo.displayField ;
39493         
39494         
39495         this.combo.wrap.addClass('x-cbarray-grp');
39496         
39497         var cbwrap = this.combo.wrap.createChild(
39498             {tag: 'div', cls: 'x-cbarray-cb'},
39499             this.combo.el.dom
39500         );
39501         
39502              
39503         this.hiddenEl = this.combo.wrap.createChild({
39504             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
39505         });
39506         this.el = this.combo.wrap.createChild({
39507             tag: 'input',  type:'hidden' , name: this.name, value : ''
39508         });
39509          //   this.el.dom.removeAttribute("name");
39510         
39511         
39512         this.outerWrap = this.combo.wrap;
39513         this.wrap = cbwrap;
39514         
39515         this.outerWrap.setWidth(this.width);
39516         this.outerWrap.dom.removeChild(this.el.dom);
39517         
39518         this.wrap.dom.appendChild(this.el.dom);
39519         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
39520         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
39521         
39522         this.combo.trigger.setStyle('position','relative');
39523         this.combo.trigger.setStyle('left', '0px');
39524         this.combo.trigger.setStyle('top', '2px');
39525         
39526         this.combo.el.setStyle('vertical-align', 'text-bottom');
39527         
39528         //this.trigger.setStyle('vertical-align', 'top');
39529         
39530         // this should use the code from combo really... on('add' ....)
39531         if (this.adder) {
39532             
39533         
39534             this.adder = this.outerWrap.createChild(
39535                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
39536             var _t = this;
39537             this.adder.on('click', function(e) {
39538                 _t.fireEvent('adderclick', this, e);
39539             }, _t);
39540         }
39541         //var _t = this;
39542         //this.adder.on('click', this.onAddClick, _t);
39543         
39544         
39545         this.combo.on('select', function(cb, rec, ix) {
39546             this.addItem(rec.data);
39547             
39548             cb.setValue('');
39549             cb.el.dom.value = '';
39550             //cb.lastData = rec.data;
39551             // add to list
39552             
39553         }, this);
39554         
39555         
39556     },
39557     
39558     
39559     getName: function()
39560     {
39561         // returns hidden if it's set..
39562         if (!this.rendered) {return ''};
39563         return  this.hiddenName ? this.hiddenName : this.name;
39564         
39565     },
39566     
39567     
39568     onResize: function(w, h){
39569         
39570         return;
39571         // not sure if this is needed..
39572         //this.combo.onResize(w,h);
39573         
39574         if(typeof w != 'number'){
39575             // we do not handle it!?!?
39576             return;
39577         }
39578         var tw = this.combo.trigger.getWidth();
39579         tw += this.addicon ? this.addicon.getWidth() : 0;
39580         tw += this.editicon ? this.editicon.getWidth() : 0;
39581         var x = w - tw;
39582         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
39583             
39584         this.combo.trigger.setStyle('left', '0px');
39585         
39586         if(this.list && this.listWidth === undefined){
39587             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
39588             this.list.setWidth(lw);
39589             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39590         }
39591         
39592     
39593         
39594     },
39595     
39596     addItem: function(rec)
39597     {
39598         var valueField = this.combo.valueField;
39599         var displayField = this.combo.displayField;
39600         if (this.items.indexOfKey(rec[valueField]) > -1) {
39601             //console.log("GOT " + rec.data.id);
39602             return;
39603         }
39604         
39605         var x = new Roo.form.ComboBoxArray.Item({
39606             //id : rec[this.idField],
39607             data : rec,
39608             displayField : displayField ,
39609             tipField : displayField ,
39610             cb : this
39611         });
39612         // use the 
39613         this.items.add(rec[valueField],x);
39614         // add it before the element..
39615         this.updateHiddenEl();
39616         x.render(this.outerWrap, this.wrap.dom);
39617         // add the image handler..
39618     },
39619     
39620     updateHiddenEl : function()
39621     {
39622         this.validate();
39623         if (!this.hiddenEl) {
39624             return;
39625         }
39626         var ar = [];
39627         var idField = this.combo.valueField;
39628         
39629         this.items.each(function(f) {
39630             ar.push(f.data[idField]);
39631            
39632         });
39633         this.hiddenEl.dom.value = ar.join(',');
39634         this.validate();
39635     },
39636     
39637     reset : function()
39638     {
39639         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
39640         this.items.each(function(f) {
39641            f.remove(); 
39642         });
39643         this.el.dom.value = '';
39644         if (this.hiddenEl) {
39645             this.hiddenEl.dom.value = '';
39646         }
39647         
39648     },
39649     getValue: function()
39650     {
39651         return this.hiddenEl ? this.hiddenEl.dom.value : '';
39652     },
39653     setValue: function(v) // not a valid action - must use addItems..
39654     {
39655          
39656         this.reset();
39657         
39658         
39659         
39660         if (this.store.isLocal && (typeof(v) == 'string')) {
39661             // then we can use the store to find the values..
39662             // comma seperated at present.. this needs to allow JSON based encoding..
39663             this.hiddenEl.value  = v;
39664             var v_ar = [];
39665             Roo.each(v.split(','), function(k) {
39666                 Roo.log("CHECK " + this.valueField + ',' + k);
39667                 var li = this.store.query(this.valueField, k);
39668                 if (!li.length) {
39669                     return;
39670                 }
39671                 add = {};
39672                 add[this.valueField] = k;
39673                 add[this.displayField] = li.item(0).data[this.displayField];
39674                 
39675                 this.addItem(add);
39676             }, this) 
39677              
39678         }
39679         if (typeof(v) == 'object') {
39680             // then let's assume it's an array of objects..
39681             Roo.each(v, function(l) {
39682                 this.addItem(l);
39683             }, this);
39684              
39685         }
39686         
39687         
39688     },
39689     setFromData: function(v)
39690     {
39691         // this recieves an object, if setValues is called.
39692         this.reset();
39693         this.el.dom.value = v[this.displayField];
39694         this.hiddenEl.dom.value = v[this.valueField];
39695         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
39696             return;
39697         }
39698         var kv = v[this.valueField];
39699         var dv = v[this.displayField];
39700         kv = typeof(kv) != 'string' ? '' : kv;
39701         dv = typeof(dv) != 'string' ? '' : dv;
39702         
39703         
39704         var keys = kv.split(',');
39705         var display = dv.split(',');
39706         for (var i = 0 ; i < keys.length; i++) {
39707             
39708             add = {};
39709             add[this.valueField] = keys[i];
39710             add[this.displayField] = display[i];
39711             this.addItem(add);
39712         }
39713       
39714         
39715     },
39716     
39717     
39718     validateValue : function(value){
39719         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
39720         
39721     }
39722     
39723 });
39724
39725
39726
39727 /**
39728  * @class Roo.form.ComboBoxArray.Item
39729  * @extends Roo.BoxComponent
39730  * A selected item in the list
39731  *  Fred [x]  Brian [x]  [Pick another |v]
39732  * 
39733  * @constructor
39734  * Create a new item.
39735  * @param {Object} config Configuration options
39736  */
39737  
39738 Roo.form.ComboBoxArray.Item = function(config) {
39739     config.id = Roo.id();
39740     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
39741 }
39742
39743 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
39744     data : {},
39745     cb: false,
39746     displayField : false,
39747     tipField : false,
39748     
39749     
39750     defaultAutoCreate : {
39751         tag: 'div',
39752         cls: 'x-cbarray-item',
39753         cn : [ 
39754             { tag: 'div' },
39755             {
39756                 tag: 'img',
39757                 width:16,
39758                 height : 16,
39759                 src : Roo.BLANK_IMAGE_URL ,
39760                 align: 'center'
39761             }
39762         ]
39763         
39764     },
39765     
39766  
39767     onRender : function(ct, position)
39768     {
39769         Roo.form.Field.superclass.onRender.call(this, ct, position);
39770         
39771         if(!this.el){
39772             var cfg = this.getAutoCreate();
39773             this.el = ct.createChild(cfg, position);
39774         }
39775         
39776         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
39777         
39778         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
39779             this.cb.renderer(this.data) :
39780             String.format('{0}',this.data[this.displayField]);
39781         
39782             
39783         this.el.child('div').dom.setAttribute('qtip',
39784                         String.format('{0}',this.data[this.tipField])
39785         );
39786         
39787         this.el.child('img').on('click', this.remove, this);
39788         
39789     },
39790    
39791     remove : function()
39792     {
39793         
39794         this.cb.items.remove(this);
39795         this.el.child('img').un('click', this.remove, this);
39796         this.el.remove();
39797         this.cb.updateHiddenEl();
39798     }
39799     
39800     
39801 });/*
39802  * Based on:
39803  * Ext JS Library 1.1.1
39804  * Copyright(c) 2006-2007, Ext JS, LLC.
39805  *
39806  * Originally Released Under LGPL - original licence link has changed is not relivant.
39807  *
39808  * Fork - LGPL
39809  * <script type="text/javascript">
39810  */
39811 /**
39812  * @class Roo.form.Checkbox
39813  * @extends Roo.form.Field
39814  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
39815  * @constructor
39816  * Creates a new Checkbox
39817  * @param {Object} config Configuration options
39818  */
39819 Roo.form.Checkbox = function(config){
39820     Roo.form.Checkbox.superclass.constructor.call(this, config);
39821     this.addEvents({
39822         /**
39823          * @event check
39824          * Fires when the checkbox is checked or unchecked.
39825              * @param {Roo.form.Checkbox} this This checkbox
39826              * @param {Boolean} checked The new checked value
39827              */
39828         check : true
39829     });
39830 };
39831
39832 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
39833     /**
39834      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
39835      */
39836     focusClass : undefined,
39837     /**
39838      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
39839      */
39840     fieldClass: "x-form-field",
39841     /**
39842      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
39843      */
39844     checked: false,
39845     /**
39846      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39847      * {tag: "input", type: "checkbox", autocomplete: "off"})
39848      */
39849     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
39850     /**
39851      * @cfg {String} boxLabel The text that appears beside the checkbox
39852      */
39853     boxLabel : "",
39854     /**
39855      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
39856      */  
39857     inputValue : '1',
39858     /**
39859      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
39860      */
39861      valueOff: '0', // value when not checked..
39862
39863     actionMode : 'viewEl', 
39864     //
39865     // private
39866     itemCls : 'x-menu-check-item x-form-item',
39867     groupClass : 'x-menu-group-item',
39868     inputType : 'hidden',
39869     
39870     
39871     inSetChecked: false, // check that we are not calling self...
39872     
39873     inputElement: false, // real input element?
39874     basedOn: false, // ????
39875     
39876     isFormField: true, // not sure where this is needed!!!!
39877
39878     onResize : function(){
39879         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
39880         if(!this.boxLabel){
39881             this.el.alignTo(this.wrap, 'c-c');
39882         }
39883     },
39884
39885     initEvents : function(){
39886         Roo.form.Checkbox.superclass.initEvents.call(this);
39887         this.el.on("click", this.onClick,  this);
39888         this.el.on("change", this.onClick,  this);
39889     },
39890
39891
39892     getResizeEl : function(){
39893         return this.wrap;
39894     },
39895
39896     getPositionEl : function(){
39897         return this.wrap;
39898     },
39899
39900     // private
39901     onRender : function(ct, position){
39902         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
39903         /*
39904         if(this.inputValue !== undefined){
39905             this.el.dom.value = this.inputValue;
39906         }
39907         */
39908         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
39909         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
39910         var viewEl = this.wrap.createChild({ 
39911             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
39912         this.viewEl = viewEl;   
39913         this.wrap.on('click', this.onClick,  this); 
39914         
39915         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
39916         this.el.on('propertychange', this.setFromHidden,  this);  //ie
39917         
39918         
39919         
39920         if(this.boxLabel){
39921             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
39922         //    viewEl.on('click', this.onClick,  this); 
39923         }
39924         //if(this.checked){
39925             this.setChecked(this.checked);
39926         //}else{
39927             //this.checked = this.el.dom;
39928         //}
39929
39930     },
39931
39932     // private
39933     initValue : Roo.emptyFn,
39934
39935     /**
39936      * Returns the checked state of the checkbox.
39937      * @return {Boolean} True if checked, else false
39938      */
39939     getValue : function(){
39940         if(this.el){
39941             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
39942         }
39943         return this.valueOff;
39944         
39945     },
39946
39947         // private
39948     onClick : function(){ 
39949         this.setChecked(!this.checked);
39950
39951         //if(this.el.dom.checked != this.checked){
39952         //    this.setValue(this.el.dom.checked);
39953        // }
39954     },
39955
39956     /**
39957      * Sets the checked state of the checkbox.
39958      * On is always based on a string comparison between inputValue and the param.
39959      * @param {Boolean/String} value - the value to set 
39960      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
39961      */
39962     setValue : function(v,suppressEvent){
39963         
39964         
39965         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
39966         //if(this.el && this.el.dom){
39967         //    this.el.dom.checked = this.checked;
39968         //    this.el.dom.defaultChecked = this.checked;
39969         //}
39970         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
39971         //this.fireEvent("check", this, this.checked);
39972     },
39973     // private..
39974     setChecked : function(state,suppressEvent)
39975     {
39976         if (this.inSetChecked) {
39977             this.checked = state;
39978             return;
39979         }
39980         
39981     
39982         if(this.wrap){
39983             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
39984         }
39985         this.checked = state;
39986         if(suppressEvent !== true){
39987             this.fireEvent('check', this, state);
39988         }
39989         this.inSetChecked = true;
39990         this.el.dom.value = state ? this.inputValue : this.valueOff;
39991         this.inSetChecked = false;
39992         
39993     },
39994     // handle setting of hidden value by some other method!!?!?
39995     setFromHidden: function()
39996     {
39997         if(!this.el){
39998             return;
39999         }
40000         //console.log("SET FROM HIDDEN");
40001         //alert('setFrom hidden');
40002         this.setValue(this.el.dom.value);
40003     },
40004     
40005     onDestroy : function()
40006     {
40007         if(this.viewEl){
40008             Roo.get(this.viewEl).remove();
40009         }
40010          
40011         Roo.form.Checkbox.superclass.onDestroy.call(this);
40012     }
40013
40014 });/*
40015  * Based on:
40016  * Ext JS Library 1.1.1
40017  * Copyright(c) 2006-2007, Ext JS, LLC.
40018  *
40019  * Originally Released Under LGPL - original licence link has changed is not relivant.
40020  *
40021  * Fork - LGPL
40022  * <script type="text/javascript">
40023  */
40024  
40025 /**
40026  * @class Roo.form.Radio
40027  * @extends Roo.form.Checkbox
40028  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40029  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40030  * @constructor
40031  * Creates a new Radio
40032  * @param {Object} config Configuration options
40033  */
40034 Roo.form.Radio = function(){
40035     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40036 };
40037 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40038     inputType: 'radio',
40039
40040     /**
40041      * If this radio is part of a group, it will return the selected value
40042      * @return {String}
40043      */
40044     getGroupValue : function(){
40045         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40046     }
40047 });//<script type="text/javascript">
40048
40049 /*
40050  * Ext JS Library 1.1.1
40051  * Copyright(c) 2006-2007, Ext JS, LLC.
40052  * licensing@extjs.com
40053  * 
40054  * http://www.extjs.com/license
40055  */
40056  
40057  /*
40058   * 
40059   * Known bugs:
40060   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40061   * - IE ? - no idea how much works there.
40062   * 
40063   * 
40064   * 
40065   */
40066  
40067
40068 /**
40069  * @class Ext.form.HtmlEditor
40070  * @extends Ext.form.Field
40071  * Provides a lightweight HTML Editor component.
40072  *
40073  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40074  * 
40075  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40076  * supported by this editor.</b><br/><br/>
40077  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40078  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40079  */
40080 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40081       /**
40082      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40083      */
40084     toolbars : false,
40085     /**
40086      * @cfg {String} createLinkText The default text for the create link prompt
40087      */
40088     createLinkText : 'Please enter the URL for the link:',
40089     /**
40090      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40091      */
40092     defaultLinkValue : 'http:/'+'/',
40093    
40094      /**
40095      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40096      *                        Roo.resizable.
40097      */
40098     resizable : false,
40099      /**
40100      * @cfg {Number} height (in pixels)
40101      */   
40102     height: 300,
40103    /**
40104      * @cfg {Number} width (in pixels)
40105      */   
40106     width: 500,
40107     
40108     /**
40109      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40110      * 
40111      */
40112     stylesheets: false,
40113     
40114     // id of frame..
40115     frameId: false,
40116     
40117     // private properties
40118     validationEvent : false,
40119     deferHeight: true,
40120     initialized : false,
40121     activated : false,
40122     sourceEditMode : false,
40123     onFocus : Roo.emptyFn,
40124     iframePad:3,
40125     hideMode:'offsets',
40126     
40127     defaultAutoCreate : { // modified by initCompnoent..
40128         tag: "textarea",
40129         style:"width:500px;height:300px;",
40130         autocomplete: "off"
40131     },
40132
40133     // private
40134     initComponent : function(){
40135         this.addEvents({
40136             /**
40137              * @event initialize
40138              * Fires when the editor is fully initialized (including the iframe)
40139              * @param {HtmlEditor} this
40140              */
40141             initialize: true,
40142             /**
40143              * @event activate
40144              * Fires when the editor is first receives the focus. Any insertion must wait
40145              * until after this event.
40146              * @param {HtmlEditor} this
40147              */
40148             activate: true,
40149              /**
40150              * @event beforesync
40151              * Fires before the textarea is updated with content from the editor iframe. Return false
40152              * to cancel the sync.
40153              * @param {HtmlEditor} this
40154              * @param {String} html
40155              */
40156             beforesync: true,
40157              /**
40158              * @event beforepush
40159              * Fires before the iframe editor is updated with content from the textarea. Return false
40160              * to cancel the push.
40161              * @param {HtmlEditor} this
40162              * @param {String} html
40163              */
40164             beforepush: true,
40165              /**
40166              * @event sync
40167              * Fires when the textarea is updated with content from the editor iframe.
40168              * @param {HtmlEditor} this
40169              * @param {String} html
40170              */
40171             sync: true,
40172              /**
40173              * @event push
40174              * Fires when the iframe editor is updated with content from the textarea.
40175              * @param {HtmlEditor} this
40176              * @param {String} html
40177              */
40178             push: true,
40179              /**
40180              * @event editmodechange
40181              * Fires when the editor switches edit modes
40182              * @param {HtmlEditor} this
40183              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40184              */
40185             editmodechange: true,
40186             /**
40187              * @event editorevent
40188              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40189              * @param {HtmlEditor} this
40190              */
40191             editorevent: true
40192         });
40193         this.defaultAutoCreate =  {
40194             tag: "textarea",
40195             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40196             autocomplete: "off"
40197         };
40198     },
40199
40200     /**
40201      * Protected method that will not generally be called directly. It
40202      * is called when the editor creates its toolbar. Override this method if you need to
40203      * add custom toolbar buttons.
40204      * @param {HtmlEditor} editor
40205      */
40206     createToolbar : function(editor){
40207         if (!editor.toolbars || !editor.toolbars.length) {
40208             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40209         }
40210         
40211         for (var i =0 ; i < editor.toolbars.length;i++) {
40212             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
40213             editor.toolbars[i].init(editor);
40214         }
40215          
40216         
40217     },
40218
40219     /**
40220      * Protected method that will not generally be called directly. It
40221      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40222      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40223      */
40224     getDocMarkup : function(){
40225         // body styles..
40226         var st = '';
40227         if (this.stylesheets === false) {
40228             
40229             Roo.get(document.head).select('style').each(function(node) {
40230                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40231             });
40232             
40233             Roo.get(document.head).select('link').each(function(node) { 
40234                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40235             });
40236             
40237         } else if (!this.stylesheets.length) {
40238                 // simple..
40239                 st = '<style type="text/css">' +
40240                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40241                    '</style>';
40242         } else {
40243             Roo.each(this.stylesheets, function(s) {
40244                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
40245             });
40246             
40247         }
40248         
40249         st +=  '<style type="text/css">' +
40250             'IMG { cursor: pointer } ' +
40251         '</style>';
40252
40253         
40254         return '<html><head>' + st  +
40255             //<style type="text/css">' +
40256             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40257             //'</style>' +
40258             ' </head><body class="roo-htmleditor-body"></body></html>';
40259     },
40260
40261     // private
40262     onRender : function(ct, position)
40263     {
40264         var _t = this;
40265         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
40266         this.el.dom.style.border = '0 none';
40267         this.el.dom.setAttribute('tabIndex', -1);
40268         this.el.addClass('x-hidden');
40269         if(Roo.isIE){ // fix IE 1px bogus margin
40270             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
40271         }
40272         this.wrap = this.el.wrap({
40273             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
40274         });
40275         
40276         if (this.resizable) {
40277             this.resizeEl = new Roo.Resizable(this.wrap, {
40278                 pinned : true,
40279                 wrap: true,
40280                 dynamic : true,
40281                 minHeight : this.height,
40282                 height: this.height,
40283                 handles : this.resizable,
40284                 width: this.width,
40285                 listeners : {
40286                     resize : function(r, w, h) {
40287                         _t.onResize(w,h); // -something
40288                     }
40289                 }
40290             });
40291             
40292         }
40293
40294         this.frameId = Roo.id();
40295         
40296         this.createToolbar(this);
40297         
40298       
40299         
40300         var iframe = this.wrap.createChild({
40301             tag: 'iframe',
40302             id: this.frameId,
40303             name: this.frameId,
40304             frameBorder : 'no',
40305             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
40306         }, this.el
40307         );
40308         
40309        // console.log(iframe);
40310         //this.wrap.dom.appendChild(iframe);
40311
40312         this.iframe = iframe.dom;
40313
40314          this.assignDocWin();
40315         
40316         this.doc.designMode = 'on';
40317        
40318         this.doc.open();
40319         this.doc.write(this.getDocMarkup());
40320         this.doc.close();
40321
40322         
40323         var task = { // must defer to wait for browser to be ready
40324             run : function(){
40325                 //console.log("run task?" + this.doc.readyState);
40326                 this.assignDocWin();
40327                 if(this.doc.body || this.doc.readyState == 'complete'){
40328                     try {
40329                         this.doc.designMode="on";
40330                     } catch (e) {
40331                         return;
40332                     }
40333                     Roo.TaskMgr.stop(task);
40334                     this.initEditor.defer(10, this);
40335                 }
40336             },
40337             interval : 10,
40338             duration:10000,
40339             scope: this
40340         };
40341         Roo.TaskMgr.start(task);
40342
40343         if(!this.width){
40344             this.setSize(this.wrap.getSize());
40345         }
40346         if (this.resizeEl) {
40347             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
40348             // should trigger onReize..
40349         }
40350     },
40351
40352     // private
40353     onResize : function(w, h)
40354     {
40355         //Roo.log('resize: ' +w + ',' + h );
40356         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
40357         if(this.el && this.iframe){
40358             if(typeof w == 'number'){
40359                 var aw = w - this.wrap.getFrameWidth('lr');
40360                 this.el.setWidth(this.adjustWidth('textarea', aw));
40361                 this.iframe.style.width = aw + 'px';
40362             }
40363             if(typeof h == 'number'){
40364                 var tbh = 0;
40365                 for (var i =0; i < this.toolbars.length;i++) {
40366                     // fixme - ask toolbars for heights?
40367                     tbh += this.toolbars[i].tb.el.getHeight();
40368                     if (this.toolbars[i].footer) {
40369                         tbh += this.toolbars[i].footer.el.getHeight();
40370                     }
40371                 }
40372                 
40373                 
40374                 
40375                 
40376                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
40377                 ah -= 5; // knock a few pixes off for look..
40378                 this.el.setHeight(this.adjustWidth('textarea', ah));
40379                 this.iframe.style.height = ah + 'px';
40380                 if(this.doc){
40381                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
40382                 }
40383             }
40384         }
40385     },
40386
40387     /**
40388      * Toggles the editor between standard and source edit mode.
40389      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
40390      */
40391     toggleSourceEdit : function(sourceEditMode){
40392         
40393         this.sourceEditMode = sourceEditMode === true;
40394         
40395         if(this.sourceEditMode){
40396           
40397             this.syncValue();
40398             this.iframe.className = 'x-hidden';
40399             this.el.removeClass('x-hidden');
40400             this.el.dom.removeAttribute('tabIndex');
40401             this.el.focus();
40402         }else{
40403              
40404             this.pushValue();
40405             this.iframe.className = '';
40406             this.el.addClass('x-hidden');
40407             this.el.dom.setAttribute('tabIndex', -1);
40408             this.deferFocus();
40409         }
40410         this.setSize(this.wrap.getSize());
40411         this.fireEvent('editmodechange', this, this.sourceEditMode);
40412     },
40413
40414     // private used internally
40415     createLink : function(){
40416         var url = prompt(this.createLinkText, this.defaultLinkValue);
40417         if(url && url != 'http:/'+'/'){
40418             this.relayCmd('createlink', url);
40419         }
40420     },
40421
40422     // private (for BoxComponent)
40423     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40424
40425     // private (for BoxComponent)
40426     getResizeEl : function(){
40427         return this.wrap;
40428     },
40429
40430     // private (for BoxComponent)
40431     getPositionEl : function(){
40432         return this.wrap;
40433     },
40434
40435     // private
40436     initEvents : function(){
40437         this.originalValue = this.getValue();
40438     },
40439
40440     /**
40441      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40442      * @method
40443      */
40444     markInvalid : Roo.emptyFn,
40445     /**
40446      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40447      * @method
40448      */
40449     clearInvalid : Roo.emptyFn,
40450
40451     setValue : function(v){
40452         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
40453         this.pushValue();
40454     },
40455
40456     /**
40457      * Protected method that will not generally be called directly. If you need/want
40458      * custom HTML cleanup, this is the method you should override.
40459      * @param {String} html The HTML to be cleaned
40460      * return {String} The cleaned HTML
40461      */
40462     cleanHtml : function(html){
40463         html = String(html);
40464         if(html.length > 5){
40465             if(Roo.isSafari){ // strip safari nonsense
40466                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
40467             }
40468         }
40469         if(html == '&nbsp;'){
40470             html = '';
40471         }
40472         return html;
40473     },
40474
40475     /**
40476      * Protected method that will not generally be called directly. Syncs the contents
40477      * of the editor iframe with the textarea.
40478      */
40479     syncValue : function(){
40480         if(this.initialized){
40481             var bd = (this.doc.body || this.doc.documentElement);
40482             //this.cleanUpPaste(); -- this is done else where and causes havoc..
40483             var html = bd.innerHTML;
40484             if(Roo.isSafari){
40485                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
40486                 var m = bs.match(/text-align:(.*?);/i);
40487                 if(m && m[1]){
40488                     html = '<div style="'+m[0]+'">' + html + '</div>';
40489                 }
40490             }
40491             html = this.cleanHtml(html);
40492             // fix up the special chars..
40493             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
40494                 return "&#"+b.charCodeAt()+";" 
40495             });
40496             if(this.fireEvent('beforesync', this, html) !== false){
40497                 this.el.dom.value = html;
40498                 this.fireEvent('sync', this, html);
40499             }
40500         }
40501     },
40502
40503     /**
40504      * Protected method that will not generally be called directly. Pushes the value of the textarea
40505      * into the iframe editor.
40506      */
40507     pushValue : function(){
40508         if(this.initialized){
40509             var v = this.el.dom.value;
40510             if(v.length < 1){
40511                 v = '&#160;';
40512             }
40513             
40514             if(this.fireEvent('beforepush', this, v) !== false){
40515                 var d = (this.doc.body || this.doc.documentElement);
40516                 d.innerHTML = v;
40517                 this.cleanUpPaste();
40518                 this.el.dom.value = d.innerHTML;
40519                 this.fireEvent('push', this, v);
40520             }
40521         }
40522     },
40523
40524     // private
40525     deferFocus : function(){
40526         this.focus.defer(10, this);
40527     },
40528
40529     // doc'ed in Field
40530     focus : function(){
40531         if(this.win && !this.sourceEditMode){
40532             this.win.focus();
40533         }else{
40534             this.el.focus();
40535         }
40536     },
40537     
40538     assignDocWin: function()
40539     {
40540         var iframe = this.iframe;
40541         
40542          if(Roo.isIE){
40543             this.doc = iframe.contentWindow.document;
40544             this.win = iframe.contentWindow;
40545         } else {
40546             if (!Roo.get(this.frameId)) {
40547                 return;
40548             }
40549             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
40550             this.win = Roo.get(this.frameId).dom.contentWindow;
40551         }
40552     },
40553     
40554     // private
40555     initEditor : function(){
40556         //console.log("INIT EDITOR");
40557         this.assignDocWin();
40558         
40559         
40560         
40561         this.doc.designMode="on";
40562         this.doc.open();
40563         this.doc.write(this.getDocMarkup());
40564         this.doc.close();
40565         
40566         var dbody = (this.doc.body || this.doc.documentElement);
40567         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
40568         // this copies styles from the containing element into thsi one..
40569         // not sure why we need all of this..
40570         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
40571         ss['background-attachment'] = 'fixed'; // w3c
40572         dbody.bgProperties = 'fixed'; // ie
40573         Roo.DomHelper.applyStyles(dbody, ss);
40574         Roo.EventManager.on(this.doc, {
40575             //'mousedown': this.onEditorEvent,
40576             'mouseup': this.onEditorEvent,
40577             'dblclick': this.onEditorEvent,
40578             'click': this.onEditorEvent,
40579             'keyup': this.onEditorEvent,
40580             buffer:100,
40581             scope: this
40582         });
40583         if(Roo.isGecko){
40584             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
40585         }
40586         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
40587             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
40588         }
40589         this.initialized = true;
40590
40591         this.fireEvent('initialize', this);
40592         this.pushValue();
40593     },
40594
40595     // private
40596     onDestroy : function(){
40597         
40598         
40599         
40600         if(this.rendered){
40601             
40602             for (var i =0; i < this.toolbars.length;i++) {
40603                 // fixme - ask toolbars for heights?
40604                 this.toolbars[i].onDestroy();
40605             }
40606             
40607             this.wrap.dom.innerHTML = '';
40608             this.wrap.remove();
40609         }
40610     },
40611
40612     // private
40613     onFirstFocus : function(){
40614         
40615         this.assignDocWin();
40616         
40617         
40618         this.activated = true;
40619         for (var i =0; i < this.toolbars.length;i++) {
40620             this.toolbars[i].onFirstFocus();
40621         }
40622        
40623         if(Roo.isGecko){ // prevent silly gecko errors
40624             this.win.focus();
40625             var s = this.win.getSelection();
40626             if(!s.focusNode || s.focusNode.nodeType != 3){
40627                 var r = s.getRangeAt(0);
40628                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
40629                 r.collapse(true);
40630                 this.deferFocus();
40631             }
40632             try{
40633                 this.execCmd('useCSS', true);
40634                 this.execCmd('styleWithCSS', false);
40635             }catch(e){}
40636         }
40637         this.fireEvent('activate', this);
40638     },
40639
40640     // private
40641     adjustFont: function(btn){
40642         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
40643         //if(Roo.isSafari){ // safari
40644         //    adjust *= 2;
40645        // }
40646         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
40647         if(Roo.isSafari){ // safari
40648             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
40649             v =  (v < 10) ? 10 : v;
40650             v =  (v > 48) ? 48 : v;
40651             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
40652             
40653         }
40654         
40655         
40656         v = Math.max(1, v+adjust);
40657         
40658         this.execCmd('FontSize', v  );
40659     },
40660
40661     onEditorEvent : function(e){
40662         this.fireEvent('editorevent', this, e);
40663       //  this.updateToolbar();
40664         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
40665     },
40666
40667     insertTag : function(tg)
40668     {
40669         // could be a bit smarter... -> wrap the current selected tRoo..
40670         
40671         this.execCmd("formatblock",   tg);
40672         
40673     },
40674     
40675     insertText : function(txt)
40676     {
40677         
40678         
40679         range = this.createRange();
40680         range.deleteContents();
40681                //alert(Sender.getAttribute('label'));
40682                
40683         range.insertNode(this.doc.createTextNode(txt));
40684     } ,
40685     
40686     // private
40687     relayBtnCmd : function(btn){
40688         this.relayCmd(btn.cmd);
40689     },
40690
40691     /**
40692      * Executes a Midas editor command on the editor document and performs necessary focus and
40693      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
40694      * @param {String} cmd The Midas command
40695      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40696      */
40697     relayCmd : function(cmd, value){
40698         this.win.focus();
40699         this.execCmd(cmd, value);
40700         this.fireEvent('editorevent', this);
40701         //this.updateToolbar();
40702         this.deferFocus();
40703     },
40704
40705     /**
40706      * Executes a Midas editor command directly on the editor document.
40707      * For visual commands, you should use {@link #relayCmd} instead.
40708      * <b>This should only be called after the editor is initialized.</b>
40709      * @param {String} cmd The Midas command
40710      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40711      */
40712     execCmd : function(cmd, value){
40713         this.doc.execCommand(cmd, false, value === undefined ? null : value);
40714         this.syncValue();
40715     },
40716  
40717  
40718    
40719     /**
40720      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
40721      * to insert tRoo.
40722      * @param {String} text | dom node.. 
40723      */
40724     insertAtCursor : function(text)
40725     {
40726         
40727         
40728         
40729         if(!this.activated){
40730             return;
40731         }
40732         /*
40733         if(Roo.isIE){
40734             this.win.focus();
40735             var r = this.doc.selection.createRange();
40736             if(r){
40737                 r.collapse(true);
40738                 r.pasteHTML(text);
40739                 this.syncValue();
40740                 this.deferFocus();
40741             
40742             }
40743             return;
40744         }
40745         */
40746         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
40747             this.win.focus();
40748             
40749             
40750             // from jquery ui (MIT licenced)
40751             var range, node;
40752             var win = this.win;
40753             
40754             if (win.getSelection && win.getSelection().getRangeAt) {
40755                 range = win.getSelection().getRangeAt(0);
40756                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
40757                 range.insertNode(node);
40758             } else if (win.document.selection && win.document.selection.createRange) {
40759                 // no firefox support
40760                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40761                 win.document.selection.createRange().pasteHTML(txt);
40762             } else {
40763                 // no firefox support
40764                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40765                 this.execCmd('InsertHTML', txt);
40766             } 
40767             
40768             this.syncValue();
40769             
40770             this.deferFocus();
40771         }
40772     },
40773  // private
40774     mozKeyPress : function(e){
40775         if(e.ctrlKey){
40776             var c = e.getCharCode(), cmd;
40777           
40778             if(c > 0){
40779                 c = String.fromCharCode(c).toLowerCase();
40780                 switch(c){
40781                     case 'b':
40782                         cmd = 'bold';
40783                         break;
40784                     case 'i':
40785                         cmd = 'italic';
40786                         break;
40787                     
40788                     case 'u':
40789                         cmd = 'underline';
40790                         break;
40791                     
40792                     case 'v':
40793                         this.cleanUpPaste.defer(100, this);
40794                         return;
40795                         
40796                 }
40797                 if(cmd){
40798                     this.win.focus();
40799                     this.execCmd(cmd);
40800                     this.deferFocus();
40801                     e.preventDefault();
40802                 }
40803                 
40804             }
40805         }
40806     },
40807
40808     // private
40809     fixKeys : function(){ // load time branching for fastest keydown performance
40810         if(Roo.isIE){
40811             return function(e){
40812                 var k = e.getKey(), r;
40813                 if(k == e.TAB){
40814                     e.stopEvent();
40815                     r = this.doc.selection.createRange();
40816                     if(r){
40817                         r.collapse(true);
40818                         r.pasteHTML('&#160;&#160;&#160;&#160;');
40819                         this.deferFocus();
40820                     }
40821                     return;
40822                 }
40823                 
40824                 if(k == e.ENTER){
40825                     r = this.doc.selection.createRange();
40826                     if(r){
40827                         var target = r.parentElement();
40828                         if(!target || target.tagName.toLowerCase() != 'li'){
40829                             e.stopEvent();
40830                             r.pasteHTML('<br />');
40831                             r.collapse(false);
40832                             r.select();
40833                         }
40834                     }
40835                 }
40836                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40837                     this.cleanUpPaste.defer(100, this);
40838                     return;
40839                 }
40840                 
40841                 
40842             };
40843         }else if(Roo.isOpera){
40844             return function(e){
40845                 var k = e.getKey();
40846                 if(k == e.TAB){
40847                     e.stopEvent();
40848                     this.win.focus();
40849                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
40850                     this.deferFocus();
40851                 }
40852                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40853                     this.cleanUpPaste.defer(100, this);
40854                     return;
40855                 }
40856                 
40857             };
40858         }else if(Roo.isSafari){
40859             return function(e){
40860                 var k = e.getKey();
40861                 
40862                 if(k == e.TAB){
40863                     e.stopEvent();
40864                     this.execCmd('InsertText','\t');
40865                     this.deferFocus();
40866                     return;
40867                 }
40868                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40869                     this.cleanUpPaste.defer(100, this);
40870                     return;
40871                 }
40872                 
40873              };
40874         }
40875     }(),
40876     
40877     getAllAncestors: function()
40878     {
40879         var p = this.getSelectedNode();
40880         var a = [];
40881         if (!p) {
40882             a.push(p); // push blank onto stack..
40883             p = this.getParentElement();
40884         }
40885         
40886         
40887         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
40888             a.push(p);
40889             p = p.parentNode;
40890         }
40891         a.push(this.doc.body);
40892         return a;
40893     },
40894     lastSel : false,
40895     lastSelNode : false,
40896     
40897     
40898     getSelection : function() 
40899     {
40900         this.assignDocWin();
40901         return Roo.isIE ? this.doc.selection : this.win.getSelection();
40902     },
40903     
40904     getSelectedNode: function() 
40905     {
40906         // this may only work on Gecko!!!
40907         
40908         // should we cache this!!!!
40909         
40910         
40911         
40912          
40913         var range = this.createRange(this.getSelection()).cloneRange();
40914         
40915         if (Roo.isIE) {
40916             var parent = range.parentElement();
40917             while (true) {
40918                 var testRange = range.duplicate();
40919                 testRange.moveToElementText(parent);
40920                 if (testRange.inRange(range)) {
40921                     break;
40922                 }
40923                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
40924                     break;
40925                 }
40926                 parent = parent.parentElement;
40927             }
40928             return parent;
40929         }
40930         
40931         // is ancestor a text element.
40932         var ac =  range.commonAncestorContainer;
40933         if (ac.nodeType == 3) {
40934             ac = ac.parentNode;
40935         }
40936         
40937         var ar = ac.childNodes;
40938          
40939         var nodes = [];
40940         var other_nodes = [];
40941         var has_other_nodes = false;
40942         for (var i=0;i<ar.length;i++) {
40943             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
40944                 continue;
40945             }
40946             // fullly contained node.
40947             
40948             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
40949                 nodes.push(ar[i]);
40950                 continue;
40951             }
40952             
40953             // probably selected..
40954             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
40955                 other_nodes.push(ar[i]);
40956                 continue;
40957             }
40958             // outer..
40959             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
40960                 continue;
40961             }
40962             
40963             
40964             has_other_nodes = true;
40965         }
40966         if (!nodes.length && other_nodes.length) {
40967             nodes= other_nodes;
40968         }
40969         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
40970             return false;
40971         }
40972         
40973         return nodes[0];
40974     },
40975     createRange: function(sel)
40976     {
40977         // this has strange effects when using with 
40978         // top toolbar - not sure if it's a great idea.
40979         //this.editor.contentWindow.focus();
40980         if (typeof sel != "undefined") {
40981             try {
40982                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
40983             } catch(e) {
40984                 return this.doc.createRange();
40985             }
40986         } else {
40987             return this.doc.createRange();
40988         }
40989     },
40990     getParentElement: function()
40991     {
40992         
40993         this.assignDocWin();
40994         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
40995         
40996         var range = this.createRange(sel);
40997          
40998         try {
40999             var p = range.commonAncestorContainer;
41000             while (p.nodeType == 3) { // text node
41001                 p = p.parentNode;
41002             }
41003             return p;
41004         } catch (e) {
41005             return null;
41006         }
41007     
41008     },
41009     /***
41010      *
41011      * Range intersection.. the hard stuff...
41012      *  '-1' = before
41013      *  '0' = hits..
41014      *  '1' = after.
41015      *         [ -- selected range --- ]
41016      *   [fail]                        [fail]
41017      *
41018      *    basically..
41019      *      if end is before start or  hits it. fail.
41020      *      if start is after end or hits it fail.
41021      *
41022      *   if either hits (but other is outside. - then it's not 
41023      *   
41024      *    
41025      **/
41026     
41027     
41028     // @see http://www.thismuchiknow.co.uk/?p=64.
41029     rangeIntersectsNode : function(range, node)
41030     {
41031         var nodeRange = node.ownerDocument.createRange();
41032         try {
41033             nodeRange.selectNode(node);
41034         } catch (e) {
41035             nodeRange.selectNodeContents(node);
41036         }
41037     
41038         var rangeStartRange = range.cloneRange();
41039         rangeStartRange.collapse(true);
41040     
41041         var rangeEndRange = range.cloneRange();
41042         rangeEndRange.collapse(false);
41043     
41044         var nodeStartRange = nodeRange.cloneRange();
41045         nodeStartRange.collapse(true);
41046     
41047         var nodeEndRange = nodeRange.cloneRange();
41048         nodeEndRange.collapse(false);
41049     
41050         return rangeStartRange.compareBoundaryPoints(
41051                  Range.START_TO_START, nodeEndRange) == -1 &&
41052                rangeEndRange.compareBoundaryPoints(
41053                  Range.START_TO_START, nodeStartRange) == 1;
41054         
41055          
41056     },
41057     rangeCompareNode : function(range, node)
41058     {
41059         var nodeRange = node.ownerDocument.createRange();
41060         try {
41061             nodeRange.selectNode(node);
41062         } catch (e) {
41063             nodeRange.selectNodeContents(node);
41064         }
41065         
41066         
41067         range.collapse(true);
41068     
41069         nodeRange.collapse(true);
41070      
41071         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41072         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41073          
41074         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41075         
41076         var nodeIsBefore   =  ss == 1;
41077         var nodeIsAfter    = ee == -1;
41078         
41079         if (nodeIsBefore && nodeIsAfter)
41080             return 0; // outer
41081         if (!nodeIsBefore && nodeIsAfter)
41082             return 1; //right trailed.
41083         
41084         if (nodeIsBefore && !nodeIsAfter)
41085             return 2;  // left trailed.
41086         // fully contined.
41087         return 3;
41088     },
41089
41090     // private? - in a new class?
41091     cleanUpPaste :  function()
41092     {
41093         // cleans up the whole document..
41094          Roo.log('cleanuppaste');
41095         this.cleanUpChildren(this.doc.body);
41096         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41097         if (clean != this.doc.body.innerHTML) {
41098             this.doc.body.innerHTML = clean;
41099         }
41100         
41101     },
41102     
41103     cleanWordChars : function(input) {
41104         var he = Roo.form.HtmlEditor;
41105     
41106         var output = input;
41107         Roo.each(he.swapCodes, function(sw) { 
41108         
41109             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41110             output = output.replace(swapper, sw[1]);
41111         });
41112         return output;
41113     },
41114     
41115     
41116     cleanUpChildren : function (n)
41117     {
41118         if (!n.childNodes.length) {
41119             return;
41120         }
41121         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41122            this.cleanUpChild(n.childNodes[i]);
41123         }
41124     },
41125     
41126     
41127         
41128     
41129     cleanUpChild : function (node)
41130     {
41131         //console.log(node);
41132         if (node.nodeName == "#text") {
41133             // clean up silly Windows -- stuff?
41134             return; 
41135         }
41136         if (node.nodeName == "#comment") {
41137             node.parentNode.removeChild(node);
41138             // clean up silly Windows -- stuff?
41139             return; 
41140         }
41141         
41142         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41143             // remove node.
41144             node.parentNode.removeChild(node);
41145             return;
41146             
41147         }
41148         
41149         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41150         
41151         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41152         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41153         
41154         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41155         //    remove_keep_children = true;
41156         //}
41157         
41158         if (remove_keep_children) {
41159             this.cleanUpChildren(node);
41160             // inserts everything just before this node...
41161             while (node.childNodes.length) {
41162                 var cn = node.childNodes[0];
41163                 node.removeChild(cn);
41164                 node.parentNode.insertBefore(cn, node);
41165             }
41166             node.parentNode.removeChild(node);
41167             return;
41168         }
41169         
41170         if (!node.attributes || !node.attributes.length) {
41171             this.cleanUpChildren(node);
41172             return;
41173         }
41174         
41175         function cleanAttr(n,v)
41176         {
41177             
41178             if (v.match(/^\./) || v.match(/^\//)) {
41179                 return;
41180             }
41181             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41182                 return;
41183             }
41184             if (v.match(/^#/)) {
41185                 return;
41186             }
41187             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
41188             node.removeAttribute(n);
41189             
41190         }
41191         
41192         function cleanStyle(n,v)
41193         {
41194             if (v.match(/expression/)) { //XSS?? should we even bother..
41195                 node.removeAttribute(n);
41196                 return;
41197             }
41198             
41199             
41200             var parts = v.split(/;/);
41201             Roo.each(parts, function(p) {
41202                 p = p.replace(/\s+/g,'');
41203                 if (!p.length) {
41204                     return true;
41205                 }
41206                 var l = p.split(':').shift().replace(/\s+/g,'');
41207                 
41208                 // only allow 'c whitelisted system attributes'
41209                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
41210                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
41211                     node.removeAttribute(n);
41212                     return false;
41213                 }
41214                 return true;
41215             });
41216             
41217             
41218         }
41219         
41220         
41221         for (var i = node.attributes.length-1; i > -1 ; i--) {
41222             var a = node.attributes[i];
41223             //console.log(a);
41224             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
41225                 node.removeAttribute(a.name);
41226                 continue;
41227             }
41228             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
41229                 cleanAttr(a.name,a.value); // fixme..
41230                 continue;
41231             }
41232             if (a.name == 'style') {
41233                 cleanStyle(a.name,a.value);
41234                 continue;
41235             }
41236             /// clean up MS crap..
41237             // tecnically this should be a list of valid class'es..
41238             
41239             
41240             if (a.name == 'class') {
41241                 if (a.value.match(/^Mso/)) {
41242                     node.className = '';
41243                 }
41244                 
41245                 if (a.value.match(/body/)) {
41246                     node.className = '';
41247                 }
41248                 continue;
41249             }
41250             
41251             // style cleanup!?
41252             // class cleanup?
41253             
41254         }
41255         
41256         
41257         this.cleanUpChildren(node);
41258         
41259         
41260     }
41261     
41262     
41263     // hide stuff that is not compatible
41264     /**
41265      * @event blur
41266      * @hide
41267      */
41268     /**
41269      * @event change
41270      * @hide
41271      */
41272     /**
41273      * @event focus
41274      * @hide
41275      */
41276     /**
41277      * @event specialkey
41278      * @hide
41279      */
41280     /**
41281      * @cfg {String} fieldClass @hide
41282      */
41283     /**
41284      * @cfg {String} focusClass @hide
41285      */
41286     /**
41287      * @cfg {String} autoCreate @hide
41288      */
41289     /**
41290      * @cfg {String} inputType @hide
41291      */
41292     /**
41293      * @cfg {String} invalidClass @hide
41294      */
41295     /**
41296      * @cfg {String} invalidText @hide
41297      */
41298     /**
41299      * @cfg {String} msgFx @hide
41300      */
41301     /**
41302      * @cfg {String} validateOnBlur @hide
41303      */
41304 });
41305
41306 Roo.form.HtmlEditor.white = [
41307         'area', 'br', 'img', 'input', 'hr', 'wbr',
41308         
41309        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
41310        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
41311        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
41312        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
41313        'table',   'ul',         'xmp', 
41314        
41315        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
41316       'thead',   'tr', 
41317      
41318       'dir', 'menu', 'ol', 'ul', 'dl',
41319        
41320       'embed',  'object'
41321 ];
41322
41323
41324 Roo.form.HtmlEditor.black = [
41325     //    'embed',  'object', // enable - backend responsiblity to clean thiese
41326         'applet', // 
41327         'base',   'basefont', 'bgsound', 'blink',  'body', 
41328         'frame',  'frameset', 'head',    'html',   'ilayer', 
41329         'iframe', 'layer',  'link',     'meta',    'object',   
41330         'script', 'style' ,'title',  'xml' // clean later..
41331 ];
41332 Roo.form.HtmlEditor.clean = [
41333     'script', 'style', 'title', 'xml'
41334 ];
41335 Roo.form.HtmlEditor.remove = [
41336     'font'
41337 ];
41338 // attributes..
41339
41340 Roo.form.HtmlEditor.ablack = [
41341     'on'
41342 ];
41343     
41344 Roo.form.HtmlEditor.aclean = [ 
41345     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
41346 ];
41347
41348 // protocols..
41349 Roo.form.HtmlEditor.pwhite= [
41350         'http',  'https',  'mailto'
41351 ];
41352
41353 // white listed style attributes.
41354 Roo.form.HtmlEditor.cwhite= [
41355         'text-align',
41356         'font-size'
41357 ];
41358
41359
41360 Roo.form.HtmlEditor.swapCodes   =[ 
41361     [    8211, "--" ], 
41362     [    8212, "--" ], 
41363     [    8216,  "'" ],  
41364     [    8217, "'" ],  
41365     [    8220, '"' ],  
41366     [    8221, '"' ],  
41367     [    8226, "*" ],  
41368     [    8230, "..." ]
41369 ]; 
41370
41371     // <script type="text/javascript">
41372 /*
41373  * Based on
41374  * Ext JS Library 1.1.1
41375  * Copyright(c) 2006-2007, Ext JS, LLC.
41376  *  
41377  
41378  */
41379
41380 /**
41381  * @class Roo.form.HtmlEditorToolbar1
41382  * Basic Toolbar
41383  * 
41384  * Usage:
41385  *
41386  new Roo.form.HtmlEditor({
41387     ....
41388     toolbars : [
41389         new Roo.form.HtmlEditorToolbar1({
41390             disable : { fonts: 1 , format: 1, ..., ... , ...],
41391             btns : [ .... ]
41392         })
41393     }
41394      
41395  * 
41396  * @cfg {Object} disable List of elements to disable..
41397  * @cfg {Array} btns List of additional buttons.
41398  * 
41399  * 
41400  * NEEDS Extra CSS? 
41401  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
41402  */
41403  
41404 Roo.form.HtmlEditor.ToolbarStandard = function(config)
41405 {
41406     
41407     Roo.apply(this, config);
41408     
41409     // default disabled, based on 'good practice'..
41410     this.disable = this.disable || {};
41411     Roo.applyIf(this.disable, {
41412         fontSize : true,
41413         colors : true,
41414         specialElements : true
41415     });
41416     
41417     
41418     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
41419     // dont call parent... till later.
41420 }
41421
41422 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
41423     
41424     tb: false,
41425     
41426     rendered: false,
41427     
41428     editor : false,
41429     /**
41430      * @cfg {Object} disable  List of toolbar elements to disable
41431          
41432      */
41433     disable : false,
41434       /**
41435      * @cfg {Array} fontFamilies An array of available font families
41436      */
41437     fontFamilies : [
41438         'Arial',
41439         'Courier New',
41440         'Tahoma',
41441         'Times New Roman',
41442         'Verdana'
41443     ],
41444     
41445     specialChars : [
41446            "&#169;",
41447           "&#174;",     
41448           "&#8482;",    
41449           "&#163;" ,    
41450          // "&#8212;",    
41451           "&#8230;",    
41452           "&#247;" ,    
41453         //  "&#225;" ,     ?? a acute?
41454            "&#8364;"    , //Euro
41455        //   "&#8220;"    ,
41456         //  "&#8221;"    ,
41457         //  "&#8226;"    ,
41458           "&#176;"  //   , // degrees
41459
41460          // "&#233;"     , // e ecute
41461          // "&#250;"     , // u ecute?
41462     ],
41463     
41464     specialElements : [
41465         {
41466             text: "Insert Table",
41467             xtype: 'MenuItem',
41468             xns : Roo.Menu,
41469             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
41470                 
41471         },
41472         {    
41473             text: "Insert Image",
41474             xtype: 'MenuItem',
41475             xns : Roo.Menu,
41476             ihtml : '<img src="about:blank"/>'
41477             
41478         }
41479         
41480          
41481     ],
41482     
41483     
41484     inputElements : [ 
41485             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
41486             "input:submit", "input:button", "select", "textarea", "label" ],
41487     formats : [
41488         ["p"] ,  
41489         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
41490         ["pre"],[ "code"], 
41491         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
41492     ],
41493      /**
41494      * @cfg {String} defaultFont default font to use.
41495      */
41496     defaultFont: 'tahoma',
41497    
41498     fontSelect : false,
41499     
41500     
41501     formatCombo : false,
41502     
41503     init : function(editor)
41504     {
41505         this.editor = editor;
41506         
41507         
41508         var fid = editor.frameId;
41509         var etb = this;
41510         function btn(id, toggle, handler){
41511             var xid = fid + '-'+ id ;
41512             return {
41513                 id : xid,
41514                 cmd : id,
41515                 cls : 'x-btn-icon x-edit-'+id,
41516                 enableToggle:toggle !== false,
41517                 scope: editor, // was editor...
41518                 handler:handler||editor.relayBtnCmd,
41519                 clickEvent:'mousedown',
41520                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
41521                 tabIndex:-1
41522             };
41523         }
41524         
41525         
41526         
41527         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
41528         this.tb = tb;
41529          // stop form submits
41530         tb.el.on('click', function(e){
41531             e.preventDefault(); // what does this do?
41532         });
41533
41534         if(!this.disable.font && !Roo.isSafari){
41535             /* why no safari for fonts
41536             editor.fontSelect = tb.el.createChild({
41537                 tag:'select',
41538                 tabIndex: -1,
41539                 cls:'x-font-select',
41540                 html: editor.createFontOptions()
41541             });
41542             editor.fontSelect.on('change', function(){
41543                 var font = editor.fontSelect.dom.value;
41544                 editor.relayCmd('fontname', font);
41545                 editor.deferFocus();
41546             }, editor);
41547             tb.add(
41548                 editor.fontSelect.dom,
41549                 '-'
41550             );
41551             */
41552         };
41553         if(!this.disable.formats){
41554             this.formatCombo = new Roo.form.ComboBox({
41555                 store: new Roo.data.SimpleStore({
41556                     id : 'tag',
41557                     fields: ['tag'],
41558                     data : this.formats // from states.js
41559                 }),
41560                 blockFocus : true,
41561                 //autoCreate : {tag: "div",  size: "20"},
41562                 displayField:'tag',
41563                 typeAhead: false,
41564                 mode: 'local',
41565                 editable : false,
41566                 triggerAction: 'all',
41567                 emptyText:'Add tag',
41568                 selectOnFocus:true,
41569                 width:135,
41570                 listeners : {
41571                     'select': function(c, r, i) {
41572                         editor.insertTag(r.get('tag'));
41573                         editor.focus();
41574                     }
41575                 }
41576
41577             });
41578             tb.addField(this.formatCombo);
41579             
41580         }
41581         
41582         if(!this.disable.format){
41583             tb.add(
41584                 btn('bold'),
41585                 btn('italic'),
41586                 btn('underline')
41587             );
41588         };
41589         if(!this.disable.fontSize){
41590             tb.add(
41591                 '-',
41592                 
41593                 
41594                 btn('increasefontsize', false, editor.adjustFont),
41595                 btn('decreasefontsize', false, editor.adjustFont)
41596             );
41597         };
41598         
41599         
41600         if(!this.disable.colors){
41601             tb.add(
41602                 '-', {
41603                     id:editor.frameId +'-forecolor',
41604                     cls:'x-btn-icon x-edit-forecolor',
41605                     clickEvent:'mousedown',
41606                     tooltip: this.buttonTips['forecolor'] || undefined,
41607                     tabIndex:-1,
41608                     menu : new Roo.menu.ColorMenu({
41609                         allowReselect: true,
41610                         focus: Roo.emptyFn,
41611                         value:'000000',
41612                         plain:true,
41613                         selectHandler: function(cp, color){
41614                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
41615                             editor.deferFocus();
41616                         },
41617                         scope: editor,
41618                         clickEvent:'mousedown'
41619                     })
41620                 }, {
41621                     id:editor.frameId +'backcolor',
41622                     cls:'x-btn-icon x-edit-backcolor',
41623                     clickEvent:'mousedown',
41624                     tooltip: this.buttonTips['backcolor'] || undefined,
41625                     tabIndex:-1,
41626                     menu : new Roo.menu.ColorMenu({
41627                         focus: Roo.emptyFn,
41628                         value:'FFFFFF',
41629                         plain:true,
41630                         allowReselect: true,
41631                         selectHandler: function(cp, color){
41632                             if(Roo.isGecko){
41633                                 editor.execCmd('useCSS', false);
41634                                 editor.execCmd('hilitecolor', color);
41635                                 editor.execCmd('useCSS', true);
41636                                 editor.deferFocus();
41637                             }else{
41638                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
41639                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
41640                                 editor.deferFocus();
41641                             }
41642                         },
41643                         scope:editor,
41644                         clickEvent:'mousedown'
41645                     })
41646                 }
41647             );
41648         };
41649         // now add all the items...
41650         
41651
41652         if(!this.disable.alignments){
41653             tb.add(
41654                 '-',
41655                 btn('justifyleft'),
41656                 btn('justifycenter'),
41657                 btn('justifyright')
41658             );
41659         };
41660
41661         //if(!Roo.isSafari){
41662             if(!this.disable.links){
41663                 tb.add(
41664                     '-',
41665                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
41666                 );
41667             };
41668
41669             if(!this.disable.lists){
41670                 tb.add(
41671                     '-',
41672                     btn('insertorderedlist'),
41673                     btn('insertunorderedlist')
41674                 );
41675             }
41676             if(!this.disable.sourceEdit){
41677                 tb.add(
41678                     '-',
41679                     btn('sourceedit', true, function(btn){
41680                         this.toggleSourceEdit(btn.pressed);
41681                     })
41682                 );
41683             }
41684         //}
41685         
41686         var smenu = { };
41687         // special menu.. - needs to be tidied up..
41688         if (!this.disable.special) {
41689             smenu = {
41690                 text: "&#169;",
41691                 cls: 'x-edit-none',
41692                 
41693                 menu : {
41694                     items : []
41695                 }
41696             };
41697             for (var i =0; i < this.specialChars.length; i++) {
41698                 smenu.menu.items.push({
41699                     
41700                     html: this.specialChars[i],
41701                     handler: function(a,b) {
41702                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
41703                         //editor.insertAtCursor(a.html);
41704                         
41705                     },
41706                     tabIndex:-1
41707                 });
41708             }
41709             
41710             
41711             tb.add(smenu);
41712             
41713             
41714         }
41715          
41716         if (!this.disable.specialElements) {
41717             var semenu = {
41718                 text: "Other;",
41719                 cls: 'x-edit-none',
41720                 menu : {
41721                     items : []
41722                 }
41723             };
41724             for (var i =0; i < this.specialElements.length; i++) {
41725                 semenu.menu.items.push(
41726                     Roo.apply({ 
41727                         handler: function(a,b) {
41728                             editor.insertAtCursor(this.ihtml);
41729                         }
41730                     }, this.specialElements[i])
41731                 );
41732                     
41733             }
41734             
41735             tb.add(semenu);
41736             
41737             
41738         }
41739          
41740         
41741         if (this.btns) {
41742             for(var i =0; i< this.btns.length;i++) {
41743                 var b = Roo.factory(this.btns[i],Roo.form);
41744                 b.cls =  'x-edit-none';
41745                 b.scope = editor;
41746                 tb.add(b);
41747             }
41748         
41749         }
41750         
41751         
41752         
41753         // disable everything...
41754         
41755         this.tb.items.each(function(item){
41756            if(item.id != editor.frameId+ '-sourceedit'){
41757                 item.disable();
41758             }
41759         });
41760         this.rendered = true;
41761         
41762         // the all the btns;
41763         editor.on('editorevent', this.updateToolbar, this);
41764         // other toolbars need to implement this..
41765         //editor.on('editmodechange', this.updateToolbar, this);
41766     },
41767     
41768     
41769     
41770     /**
41771      * Protected method that will not generally be called directly. It triggers
41772      * a toolbar update by reading the markup state of the current selection in the editor.
41773      */
41774     updateToolbar: function(){
41775
41776         if(!this.editor.activated){
41777             this.editor.onFirstFocus();
41778             return;
41779         }
41780
41781         var btns = this.tb.items.map, 
41782             doc = this.editor.doc,
41783             frameId = this.editor.frameId;
41784
41785         if(!this.disable.font && !Roo.isSafari){
41786             /*
41787             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
41788             if(name != this.fontSelect.dom.value){
41789                 this.fontSelect.dom.value = name;
41790             }
41791             */
41792         }
41793         if(!this.disable.format){
41794             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
41795             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
41796             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
41797         }
41798         if(!this.disable.alignments){
41799             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
41800             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
41801             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
41802         }
41803         if(!Roo.isSafari && !this.disable.lists){
41804             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
41805             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
41806         }
41807         
41808         var ans = this.editor.getAllAncestors();
41809         if (this.formatCombo) {
41810             
41811             
41812             var store = this.formatCombo.store;
41813             this.formatCombo.setValue("");
41814             for (var i =0; i < ans.length;i++) {
41815                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
41816                     // select it..
41817                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
41818                     break;
41819                 }
41820             }
41821         }
41822         
41823         
41824         
41825         // hides menus... - so this cant be on a menu...
41826         Roo.menu.MenuMgr.hideAll();
41827
41828         //this.editorsyncValue();
41829     },
41830    
41831     
41832     createFontOptions : function(){
41833         var buf = [], fs = this.fontFamilies, ff, lc;
41834         for(var i = 0, len = fs.length; i< len; i++){
41835             ff = fs[i];
41836             lc = ff.toLowerCase();
41837             buf.push(
41838                 '<option value="',lc,'" style="font-family:',ff,';"',
41839                     (this.defaultFont == lc ? ' selected="true">' : '>'),
41840                     ff,
41841                 '</option>'
41842             );
41843         }
41844         return buf.join('');
41845     },
41846     
41847     toggleSourceEdit : function(sourceEditMode){
41848         if(sourceEditMode === undefined){
41849             sourceEditMode = !this.sourceEditMode;
41850         }
41851         this.sourceEditMode = sourceEditMode === true;
41852         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
41853         // just toggle the button?
41854         if(btn.pressed !== this.editor.sourceEditMode){
41855             btn.toggle(this.editor.sourceEditMode);
41856             return;
41857         }
41858         
41859         if(this.sourceEditMode){
41860             this.tb.items.each(function(item){
41861                 if(item.cmd != 'sourceedit'){
41862                     item.disable();
41863                 }
41864             });
41865           
41866         }else{
41867             if(this.initialized){
41868                 this.tb.items.each(function(item){
41869                     item.enable();
41870                 });
41871             }
41872             
41873         }
41874         // tell the editor that it's been pressed..
41875         this.editor.toggleSourceEdit(sourceEditMode);
41876        
41877     },
41878      /**
41879      * Object collection of toolbar tooltips for the buttons in the editor. The key
41880      * is the command id associated with that button and the value is a valid QuickTips object.
41881      * For example:
41882 <pre><code>
41883 {
41884     bold : {
41885         title: 'Bold (Ctrl+B)',
41886         text: 'Make the selected text bold.',
41887         cls: 'x-html-editor-tip'
41888     },
41889     italic : {
41890         title: 'Italic (Ctrl+I)',
41891         text: 'Make the selected text italic.',
41892         cls: 'x-html-editor-tip'
41893     },
41894     ...
41895 </code></pre>
41896     * @type Object
41897      */
41898     buttonTips : {
41899         bold : {
41900             title: 'Bold (Ctrl+B)',
41901             text: 'Make the selected text bold.',
41902             cls: 'x-html-editor-tip'
41903         },
41904         italic : {
41905             title: 'Italic (Ctrl+I)',
41906             text: 'Make the selected text italic.',
41907             cls: 'x-html-editor-tip'
41908         },
41909         underline : {
41910             title: 'Underline (Ctrl+U)',
41911             text: 'Underline the selected text.',
41912             cls: 'x-html-editor-tip'
41913         },
41914         increasefontsize : {
41915             title: 'Grow Text',
41916             text: 'Increase the font size.',
41917             cls: 'x-html-editor-tip'
41918         },
41919         decreasefontsize : {
41920             title: 'Shrink Text',
41921             text: 'Decrease the font size.',
41922             cls: 'x-html-editor-tip'
41923         },
41924         backcolor : {
41925             title: 'Text Highlight Color',
41926             text: 'Change the background color of the selected text.',
41927             cls: 'x-html-editor-tip'
41928         },
41929         forecolor : {
41930             title: 'Font Color',
41931             text: 'Change the color of the selected text.',
41932             cls: 'x-html-editor-tip'
41933         },
41934         justifyleft : {
41935             title: 'Align Text Left',
41936             text: 'Align text to the left.',
41937             cls: 'x-html-editor-tip'
41938         },
41939         justifycenter : {
41940             title: 'Center Text',
41941             text: 'Center text in the editor.',
41942             cls: 'x-html-editor-tip'
41943         },
41944         justifyright : {
41945             title: 'Align Text Right',
41946             text: 'Align text to the right.',
41947             cls: 'x-html-editor-tip'
41948         },
41949         insertunorderedlist : {
41950             title: 'Bullet List',
41951             text: 'Start a bulleted list.',
41952             cls: 'x-html-editor-tip'
41953         },
41954         insertorderedlist : {
41955             title: 'Numbered List',
41956             text: 'Start a numbered list.',
41957             cls: 'x-html-editor-tip'
41958         },
41959         createlink : {
41960             title: 'Hyperlink',
41961             text: 'Make the selected text a hyperlink.',
41962             cls: 'x-html-editor-tip'
41963         },
41964         sourceedit : {
41965             title: 'Source Edit',
41966             text: 'Switch to source editing mode.',
41967             cls: 'x-html-editor-tip'
41968         }
41969     },
41970     // private
41971     onDestroy : function(){
41972         if(this.rendered){
41973             
41974             this.tb.items.each(function(item){
41975                 if(item.menu){
41976                     item.menu.removeAll();
41977                     if(item.menu.el){
41978                         item.menu.el.destroy();
41979                     }
41980                 }
41981                 item.destroy();
41982             });
41983              
41984         }
41985     },
41986     onFirstFocus: function() {
41987         this.tb.items.each(function(item){
41988            item.enable();
41989         });
41990     }
41991 });
41992
41993
41994
41995
41996 // <script type="text/javascript">
41997 /*
41998  * Based on
41999  * Ext JS Library 1.1.1
42000  * Copyright(c) 2006-2007, Ext JS, LLC.
42001  *  
42002  
42003  */
42004
42005  
42006 /**
42007  * @class Roo.form.HtmlEditor.ToolbarContext
42008  * Context Toolbar
42009  * 
42010  * Usage:
42011  *
42012  new Roo.form.HtmlEditor({
42013     ....
42014     toolbars : [
42015         { xtype: 'ToolbarStandard', styles : {} }
42016         { xtype: 'ToolbarContext', disable : {} }
42017     ]
42018 })
42019
42020      
42021  * 
42022  * @config : {Object} disable List of elements to disable.. (not done yet.)
42023  * @config : {Object} styles  Map of styles available.
42024  * 
42025  */
42026
42027 Roo.form.HtmlEditor.ToolbarContext = function(config)
42028 {
42029     
42030     Roo.apply(this, config);
42031     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42032     // dont call parent... till later.
42033     this.styles = this.styles || {};
42034 }
42035 Roo.form.HtmlEditor.ToolbarContext.types = {
42036     'IMG' : {
42037         width : {
42038             title: "Width",
42039             width: 40
42040         },
42041         height:  {
42042             title: "Height",
42043             width: 40
42044         },
42045         align: {
42046             title: "Align",
42047             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42048             width : 80
42049             
42050         },
42051         border: {
42052             title: "Border",
42053             width: 40
42054         },
42055         alt: {
42056             title: "Alt",
42057             width: 120
42058         },
42059         src : {
42060             title: "Src",
42061             width: 220
42062         }
42063         
42064     },
42065     'A' : {
42066         name : {
42067             title: "Name",
42068             width: 50
42069         },
42070         href:  {
42071             title: "Href",
42072             width: 220
42073         } // border?
42074         
42075     },
42076     'TABLE' : {
42077         rows : {
42078             title: "Rows",
42079             width: 20
42080         },
42081         cols : {
42082             title: "Cols",
42083             width: 20
42084         },
42085         width : {
42086             title: "Width",
42087             width: 40
42088         },
42089         height : {
42090             title: "Height",
42091             width: 40
42092         },
42093         border : {
42094             title: "Border",
42095             width: 20
42096         }
42097     },
42098     'TD' : {
42099         width : {
42100             title: "Width",
42101             width: 40
42102         },
42103         height : {
42104             title: "Height",
42105             width: 40
42106         },   
42107         align: {
42108             title: "Align",
42109             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42110             width: 80
42111         },
42112         valign: {
42113             title: "Valign",
42114             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
42115             width: 80
42116         },
42117         colspan: {
42118             title: "Colspan",
42119             width: 20
42120             
42121         }
42122     },
42123     'INPUT' : {
42124         name : {
42125             title: "name",
42126             width: 120
42127         },
42128         value : {
42129             title: "Value",
42130             width: 120
42131         },
42132         width : {
42133             title: "Width",
42134             width: 40
42135         }
42136     },
42137     'LABEL' : {
42138         'for' : {
42139             title: "For",
42140             width: 120
42141         }
42142     },
42143     'TEXTAREA' : {
42144           name : {
42145             title: "name",
42146             width: 120
42147         },
42148         rows : {
42149             title: "Rows",
42150             width: 20
42151         },
42152         cols : {
42153             title: "Cols",
42154             width: 20
42155         }
42156     },
42157     'SELECT' : {
42158         name : {
42159             title: "name",
42160             width: 120
42161         },
42162         selectoptions : {
42163             title: "Options",
42164             width: 200
42165         }
42166     },
42167     
42168     // should we really allow this??
42169     // should this just be 
42170     'BODY' : {
42171         title : {
42172             title: "title",
42173             width: 200,
42174             disabled : true
42175         }
42176     },
42177     '*' : {
42178         // empty..
42179     }
42180 };
42181
42182
42183
42184 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
42185     
42186     tb: false,
42187     
42188     rendered: false,
42189     
42190     editor : false,
42191     /**
42192      * @cfg {Object} disable  List of toolbar elements to disable
42193          
42194      */
42195     disable : false,
42196     /**
42197      * @cfg {Object} styles List of styles 
42198      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
42199      *
42200      * These must be defined in the page, so they get rendered correctly..
42201      * .headline { }
42202      * TD.underline { }
42203      * 
42204      */
42205     styles : false,
42206     
42207     
42208     
42209     toolbars : false,
42210     
42211     init : function(editor)
42212     {
42213         this.editor = editor;
42214         
42215         
42216         var fid = editor.frameId;
42217         var etb = this;
42218         function btn(id, toggle, handler){
42219             var xid = fid + '-'+ id ;
42220             return {
42221                 id : xid,
42222                 cmd : id,
42223                 cls : 'x-btn-icon x-edit-'+id,
42224                 enableToggle:toggle !== false,
42225                 scope: editor, // was editor...
42226                 handler:handler||editor.relayBtnCmd,
42227                 clickEvent:'mousedown',
42228                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42229                 tabIndex:-1
42230             };
42231         }
42232         // create a new element.
42233         var wdiv = editor.wrap.createChild({
42234                 tag: 'div'
42235             }, editor.wrap.dom.firstChild.nextSibling, true);
42236         
42237         // can we do this more than once??
42238         
42239          // stop form submits
42240       
42241  
42242         // disable everything...
42243         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42244         this.toolbars = {};
42245            
42246         for (var i in  ty) {
42247           
42248             this.toolbars[i] = this.buildToolbar(ty[i],i);
42249         }
42250         this.tb = this.toolbars.BODY;
42251         this.tb.el.show();
42252         this.buildFooter();
42253         this.footer.show();
42254         editor.on('hide', function( ) { this.footer.hide() }, this);
42255         editor.on('show', function( ) { this.footer.show() }, this);
42256         
42257          
42258         this.rendered = true;
42259         
42260         // the all the btns;
42261         editor.on('editorevent', this.updateToolbar, this);
42262         // other toolbars need to implement this..
42263         //editor.on('editmodechange', this.updateToolbar, this);
42264     },
42265     
42266     
42267     
42268     /**
42269      * Protected method that will not generally be called directly. It triggers
42270      * a toolbar update by reading the markup state of the current selection in the editor.
42271      */
42272     updateToolbar: function(editor,ev,sel){
42273
42274         //Roo.log(ev);
42275         // capture mouse up - this is handy for selecting images..
42276         // perhaps should go somewhere else...
42277         if(!this.editor.activated){
42278              this.editor.onFirstFocus();
42279             return;
42280         }
42281         
42282         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
42283         // selectNode - might want to handle IE?
42284         if (ev &&
42285             (ev.type == 'mouseup' || ev.type == 'click' ) &&
42286             ev.target && ev.target.tagName == 'IMG') {
42287             // they have click on an image...
42288             // let's see if we can change the selection...
42289             sel = ev.target;
42290          
42291               var nodeRange = sel.ownerDocument.createRange();
42292             try {
42293                 nodeRange.selectNode(sel);
42294             } catch (e) {
42295                 nodeRange.selectNodeContents(sel);
42296             }
42297             //nodeRange.collapse(true);
42298             var s = editor.win.getSelection();
42299             s.removeAllRanges();
42300             s.addRange(nodeRange);
42301         }  
42302         
42303       
42304         var updateFooter = sel ? false : true;
42305         
42306         
42307         var ans = this.editor.getAllAncestors();
42308         
42309         // pick
42310         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42311         
42312         if (!sel) { 
42313             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
42314             sel = sel ? sel : this.editor.doc.body;
42315             sel = sel.tagName.length ? sel : this.editor.doc.body;
42316             
42317         }
42318         // pick a menu that exists..
42319         var tn = sel.tagName.toUpperCase();
42320         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
42321         
42322         tn = sel.tagName.toUpperCase();
42323         
42324         var lastSel = this.tb.selectedNode
42325         
42326         this.tb.selectedNode = sel;
42327         
42328         // if current menu does not match..
42329         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
42330                 
42331             this.tb.el.hide();
42332             ///console.log("show: " + tn);
42333             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
42334             this.tb.el.show();
42335             // update name
42336             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
42337             
42338             
42339             // update attributes
42340             if (this.tb.fields) {
42341                 this.tb.fields.each(function(e) {
42342                    e.setValue(sel.getAttribute(e.attrname));
42343                 });
42344             }
42345             
42346             var hasStyles = false;
42347             for(var i in this.styles) {
42348                 hasStyles = true;
42349                 break;
42350             }
42351             
42352             // update styles
42353             if (hasStyles) { 
42354                 var st = this.tb.fields.item(0);
42355                 
42356                 st.store.removeAll();
42357                
42358                 
42359                 var cn = sel.className.split(/\s+/);
42360                 
42361                 var avs = [];
42362                 if (this.styles['*']) {
42363                     
42364                     Roo.each(this.styles['*'], function(v) {
42365                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42366                     });
42367                 }
42368                 if (this.styles[tn]) { 
42369                     Roo.each(this.styles[tn], function(v) {
42370                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42371                     });
42372                 }
42373                 
42374                 st.store.loadData(avs);
42375                 st.collapse();
42376                 st.setValue(cn);
42377             }
42378             // flag our selected Node.
42379             this.tb.selectedNode = sel;
42380            
42381            
42382             Roo.menu.MenuMgr.hideAll();
42383
42384         }
42385         
42386         if (!updateFooter) {
42387             //this.footDisp.dom.innerHTML = ''; 
42388             return;
42389         }
42390         // update the footer
42391         //
42392         var html = '';
42393         
42394         this.footerEls = ans.reverse();
42395         Roo.each(this.footerEls, function(a,i) {
42396             if (!a) { return; }
42397             html += html.length ? ' &gt; '  :  '';
42398             
42399             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
42400             
42401         });
42402        
42403         // 
42404         var sz = this.footDisp.up('td').getSize();
42405         this.footDisp.dom.style.width = (sz.width -10) + 'px';
42406         this.footDisp.dom.style.marginLeft = '5px';
42407         
42408         this.footDisp.dom.style.overflow = 'hidden';
42409         
42410         this.footDisp.dom.innerHTML = html;
42411             
42412         //this.editorsyncValue();
42413     },
42414      
42415     
42416    
42417        
42418     // private
42419     onDestroy : function(){
42420         if(this.rendered){
42421             
42422             this.tb.items.each(function(item){
42423                 if(item.menu){
42424                     item.menu.removeAll();
42425                     if(item.menu.el){
42426                         item.menu.el.destroy();
42427                     }
42428                 }
42429                 item.destroy();
42430             });
42431              
42432         }
42433     },
42434     onFirstFocus: function() {
42435         // need to do this for all the toolbars..
42436         this.tb.items.each(function(item){
42437            item.enable();
42438         });
42439     },
42440     buildToolbar: function(tlist, nm)
42441     {
42442         var editor = this.editor;
42443          // create a new element.
42444         var wdiv = editor.wrap.createChild({
42445                 tag: 'div'
42446             }, editor.wrap.dom.firstChild.nextSibling, true);
42447         
42448        
42449         var tb = new Roo.Toolbar(wdiv);
42450         // add the name..
42451         
42452         tb.add(nm+ ":&nbsp;");
42453         
42454         var styles = [];
42455         for(var i in this.styles) {
42456             styles.push(i);
42457         }
42458         
42459         // styles...
42460         if (styles && styles.length) {
42461             
42462             // this needs a multi-select checkbox...
42463             tb.addField( new Roo.form.ComboBox({
42464                 store: new Roo.data.SimpleStore({
42465                     id : 'val',
42466                     fields: ['val', 'selected'],
42467                     data : [] 
42468                 }),
42469                 name : '-roo-edit-className',
42470                 attrname : 'className',
42471                 displayField:'val',
42472                 typeAhead: false,
42473                 mode: 'local',
42474                 editable : false,
42475                 triggerAction: 'all',
42476                 emptyText:'Select Style',
42477                 selectOnFocus:true,
42478                 width: 130,
42479                 listeners : {
42480                     'select': function(c, r, i) {
42481                         // initial support only for on class per el..
42482                         tb.selectedNode.className =  r ? r.get('val') : '';
42483                         editor.syncValue();
42484                     }
42485                 }
42486     
42487             }));
42488         }
42489             
42490         
42491         
42492         for (var i in tlist) {
42493             
42494             var item = tlist[i];
42495             tb.add(item.title + ":&nbsp;");
42496             
42497             
42498             
42499             
42500             if (item.opts) {
42501                 // opts == pulldown..
42502                 tb.addField( new Roo.form.ComboBox({
42503                     store: new Roo.data.SimpleStore({
42504                         id : 'val',
42505                         fields: ['val'],
42506                         data : item.opts  
42507                     }),
42508                     name : '-roo-edit-' + i,
42509                     attrname : i,
42510                     displayField:'val',
42511                     typeAhead: false,
42512                     mode: 'local',
42513                     editable : false,
42514                     triggerAction: 'all',
42515                     emptyText:'Select',
42516                     selectOnFocus:true,
42517                     width: item.width ? item.width  : 130,
42518                     listeners : {
42519                         'select': function(c, r, i) {
42520                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
42521                         }
42522                     }
42523
42524                 }));
42525                 continue;
42526                     
42527                  
42528                 
42529                 tb.addField( new Roo.form.TextField({
42530                     name: i,
42531                     width: 100,
42532                     //allowBlank:false,
42533                     value: ''
42534                 }));
42535                 continue;
42536             }
42537             tb.addField( new Roo.form.TextField({
42538                 name: '-roo-edit-' + i,
42539                 attrname : i,
42540                 
42541                 width: item.width,
42542                 //allowBlank:true,
42543                 value: '',
42544                 listeners: {
42545                     'change' : function(f, nv, ov) {
42546                         tb.selectedNode.setAttribute(f.attrname, nv);
42547                     }
42548                 }
42549             }));
42550              
42551         }
42552         tb.addFill();
42553         var _this = this;
42554         tb.addButton( {
42555             text: 'Remove Tag',
42556     
42557             listeners : {
42558                 click : function ()
42559                 {
42560                     // remove
42561                     // undo does not work.
42562                      
42563                     var sn = tb.selectedNode;
42564                     Roo.log(sn);
42565                     var pn = sn.parentNode;
42566                     
42567                     var stn =  sn.childNodes[0];
42568                     var en = sn.childNodes[sn.childNodes.length - 1 ];
42569                     while (sn.childNodes.length) {
42570                         var node = sn.childNodes[0];
42571                         sn.removeChild(node);
42572                         Roo.log(node);
42573                         pn.insertBefore(node, sn);
42574                         
42575                     }
42576                     pn.removeChild(sn);
42577                     var range = editor.createRange();
42578         
42579                     range.setStart(stn,0);
42580                     range.setEnd(en,0); //????
42581                     //range.selectNode(sel);
42582                     
42583                     
42584                     var selection = editor.getSelection();
42585                     selection.removeAllRanges();
42586                     selection.addRange(range);
42587                     
42588                     
42589                     
42590                     //_this.updateToolbar(null, null, pn);
42591                     _this.updateToolbar(null, null, null);
42592                     this.footDisp.dom.innerHTML = ''; 
42593                 }
42594             }
42595             
42596                     
42597                 
42598             
42599         });
42600         
42601         
42602         tb.el.on('click', function(e){
42603             e.preventDefault(); // what does this do?
42604         });
42605         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
42606         tb.el.hide();
42607         tb.name = nm;
42608         // dont need to disable them... as they will get hidden
42609         return tb;
42610          
42611         
42612     },
42613     buildFooter : function()
42614     {
42615         
42616         var fel = this.editor.wrap.createChild();
42617         this.footer = new Roo.Toolbar(fel);
42618         // toolbar has scrolly on left / right?
42619         var footDisp= new Roo.Toolbar.Fill();
42620         var _t = this;
42621         this.footer.add(
42622             {
42623                 text : '&lt;',
42624                 xtype: 'Button',
42625                 handler : function() {
42626                     _t.footDisp.scrollTo('left',0,true)
42627                 }
42628             }
42629         );
42630         this.footer.add( footDisp );
42631         this.footer.add( 
42632             {
42633                 text : '&gt;',
42634                 xtype: 'Button',
42635                 handler : function() {
42636                     // no animation..
42637                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
42638                 }
42639             }
42640         );
42641         var fel = Roo.get(footDisp.el);
42642         fel.addClass('x-editor-context');
42643         this.footDispWrap = fel; 
42644         this.footDispWrap.overflow  = 'hidden';
42645         
42646         this.footDisp = fel.createChild();
42647         this.footDispWrap.on('click', this.onContextClick, this)
42648         
42649         
42650     },
42651     onContextClick : function (ev,dom)
42652     {
42653         ev.preventDefault();
42654         var  cn = dom.className;
42655         Roo.log(cn);
42656         if (!cn.match(/x-ed-loc-/)) {
42657             return;
42658         }
42659         var n = cn.split('-').pop();
42660         var ans = this.footerEls;
42661         var sel = ans[n];
42662         
42663          // pick
42664         var range = this.editor.createRange();
42665         
42666         range.selectNodeContents(sel);
42667         //range.selectNode(sel);
42668         
42669         
42670         var selection = this.editor.getSelection();
42671         selection.removeAllRanges();
42672         selection.addRange(range);
42673         
42674         
42675         
42676         this.updateToolbar(null, null, sel);
42677         
42678         
42679     }
42680     
42681     
42682     
42683     
42684     
42685 });
42686
42687
42688
42689
42690
42691 /*
42692  * Based on:
42693  * Ext JS Library 1.1.1
42694  * Copyright(c) 2006-2007, Ext JS, LLC.
42695  *
42696  * Originally Released Under LGPL - original licence link has changed is not relivant.
42697  *
42698  * Fork - LGPL
42699  * <script type="text/javascript">
42700  */
42701  
42702 /**
42703  * @class Roo.form.BasicForm
42704  * @extends Roo.util.Observable
42705  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
42706  * @constructor
42707  * @param {String/HTMLElement/Roo.Element} el The form element or its id
42708  * @param {Object} config Configuration options
42709  */
42710 Roo.form.BasicForm = function(el, config){
42711     this.allItems = [];
42712     this.childForms = [];
42713     Roo.apply(this, config);
42714     /*
42715      * The Roo.form.Field items in this form.
42716      * @type MixedCollection
42717      */
42718      
42719      
42720     this.items = new Roo.util.MixedCollection(false, function(o){
42721         return o.id || (o.id = Roo.id());
42722     });
42723     this.addEvents({
42724         /**
42725          * @event beforeaction
42726          * Fires before any action is performed. Return false to cancel the action.
42727          * @param {Form} this
42728          * @param {Action} action The action to be performed
42729          */
42730         beforeaction: true,
42731         /**
42732          * @event actionfailed
42733          * Fires when an action fails.
42734          * @param {Form} this
42735          * @param {Action} action The action that failed
42736          */
42737         actionfailed : true,
42738         /**
42739          * @event actioncomplete
42740          * Fires when an action is completed.
42741          * @param {Form} this
42742          * @param {Action} action The action that completed
42743          */
42744         actioncomplete : true
42745     });
42746     if(el){
42747         this.initEl(el);
42748     }
42749     Roo.form.BasicForm.superclass.constructor.call(this);
42750 };
42751
42752 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
42753     /**
42754      * @cfg {String} method
42755      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
42756      */
42757     /**
42758      * @cfg {DataReader} reader
42759      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
42760      * This is optional as there is built-in support for processing JSON.
42761      */
42762     /**
42763      * @cfg {DataReader} errorReader
42764      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
42765      * This is completely optional as there is built-in support for processing JSON.
42766      */
42767     /**
42768      * @cfg {String} url
42769      * The URL to use for form actions if one isn't supplied in the action options.
42770      */
42771     /**
42772      * @cfg {Boolean} fileUpload
42773      * Set to true if this form is a file upload.
42774      */
42775      
42776     /**
42777      * @cfg {Object} baseParams
42778      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
42779      */
42780      /**
42781      
42782     /**
42783      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
42784      */
42785     timeout: 30,
42786
42787     // private
42788     activeAction : null,
42789
42790     /**
42791      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
42792      * or setValues() data instead of when the form was first created.
42793      */
42794     trackResetOnLoad : false,
42795     
42796     
42797     /**
42798      * childForms - used for multi-tab forms
42799      * @type {Array}
42800      */
42801     childForms : false,
42802     
42803     /**
42804      * allItems - full list of fields.
42805      * @type {Array}
42806      */
42807     allItems : false,
42808     
42809     /**
42810      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
42811      * element by passing it or its id or mask the form itself by passing in true.
42812      * @type Mixed
42813      */
42814     waitMsgTarget : false,
42815
42816     // private
42817     initEl : function(el){
42818         this.el = Roo.get(el);
42819         this.id = this.el.id || Roo.id();
42820         this.el.on('submit', this.onSubmit, this);
42821         this.el.addClass('x-form');
42822     },
42823
42824     // private
42825     onSubmit : function(e){
42826         e.stopEvent();
42827     },
42828
42829     /**
42830      * Returns true if client-side validation on the form is successful.
42831      * @return Boolean
42832      */
42833     isValid : function(){
42834         var valid = true;
42835         this.items.each(function(f){
42836            if(!f.validate()){
42837                valid = false;
42838            }
42839         });
42840         return valid;
42841     },
42842
42843     /**
42844      * Returns true if any fields in this form have changed since their original load.
42845      * @return Boolean
42846      */
42847     isDirty : function(){
42848         var dirty = false;
42849         this.items.each(function(f){
42850            if(f.isDirty()){
42851                dirty = true;
42852                return false;
42853            }
42854         });
42855         return dirty;
42856     },
42857
42858     /**
42859      * Performs a predefined action (submit or load) or custom actions you define on this form.
42860      * @param {String} actionName The name of the action type
42861      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
42862      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
42863      * accept other config options):
42864      * <pre>
42865 Property          Type             Description
42866 ----------------  ---------------  ----------------------------------------------------------------------------------
42867 url               String           The url for the action (defaults to the form's url)
42868 method            String           The form method to use (defaults to the form's method, or POST if not defined)
42869 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
42870 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
42871                                    validate the form on the client (defaults to false)
42872      * </pre>
42873      * @return {BasicForm} this
42874      */
42875     doAction : function(action, options){
42876         if(typeof action == 'string'){
42877             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
42878         }
42879         if(this.fireEvent('beforeaction', this, action) !== false){
42880             this.beforeAction(action);
42881             action.run.defer(100, action);
42882         }
42883         return this;
42884     },
42885
42886     /**
42887      * Shortcut to do a submit action.
42888      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
42889      * @return {BasicForm} this
42890      */
42891     submit : function(options){
42892         this.doAction('submit', options);
42893         return this;
42894     },
42895
42896     /**
42897      * Shortcut to do a load action.
42898      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
42899      * @return {BasicForm} this
42900      */
42901     load : function(options){
42902         this.doAction('load', options);
42903         return this;
42904     },
42905
42906     /**
42907      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
42908      * @param {Record} record The record to edit
42909      * @return {BasicForm} this
42910      */
42911     updateRecord : function(record){
42912         record.beginEdit();
42913         var fs = record.fields;
42914         fs.each(function(f){
42915             var field = this.findField(f.name);
42916             if(field){
42917                 record.set(f.name, field.getValue());
42918             }
42919         }, this);
42920         record.endEdit();
42921         return this;
42922     },
42923
42924     /**
42925      * Loads an Roo.data.Record into this form.
42926      * @param {Record} record The record to load
42927      * @return {BasicForm} this
42928      */
42929     loadRecord : function(record){
42930         this.setValues(record.data);
42931         return this;
42932     },
42933
42934     // private
42935     beforeAction : function(action){
42936         var o = action.options;
42937         
42938        
42939         if(this.waitMsgTarget === true){
42940             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
42941         }else if(this.waitMsgTarget){
42942             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
42943             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
42944         }else {
42945             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
42946         }
42947          
42948     },
42949
42950     // private
42951     afterAction : function(action, success){
42952         this.activeAction = null;
42953         var o = action.options;
42954         
42955         if(this.waitMsgTarget === true){
42956             this.el.unmask();
42957         }else if(this.waitMsgTarget){
42958             this.waitMsgTarget.unmask();
42959         }else{
42960             Roo.MessageBox.updateProgress(1);
42961             Roo.MessageBox.hide();
42962         }
42963          
42964         if(success){
42965             if(o.reset){
42966                 this.reset();
42967             }
42968             Roo.callback(o.success, o.scope, [this, action]);
42969             this.fireEvent('actioncomplete', this, action);
42970             
42971         }else{
42972             
42973             // failure condition..
42974             // we have a scenario where updates need confirming.
42975             // eg. if a locking scenario exists..
42976             // we look for { errors : { needs_confirm : true }} in the response.
42977             if (
42978                 (typeof(action.result) != 'undefined')  &&
42979                 (typeof(action.result.errors) != 'undefined')  &&
42980                 (typeof(action.result.errors.needs_confirm) != 'undefined')
42981            ){
42982                 var _t = this;
42983                 Roo.MessageBox.confirm(
42984                     "Change requires confirmation",
42985                     action.result.errorMsg,
42986                     function(r) {
42987                         if (r != 'yes') {
42988                             return;
42989                         }
42990                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
42991                     }
42992                     
42993                 );
42994                 
42995                 
42996                 
42997                 return;
42998             }
42999             
43000             Roo.callback(o.failure, o.scope, [this, action]);
43001             // show an error message if no failed handler is set..
43002             if (!this.hasListener('actionfailed')) {
43003                 Roo.MessageBox.alert("Error",
43004                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43005                         action.result.errorMsg :
43006                         "Saving Failed, please check your entries or try again"
43007                 );
43008             }
43009             
43010             this.fireEvent('actionfailed', this, action);
43011         }
43012         
43013     },
43014
43015     /**
43016      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43017      * @param {String} id The value to search for
43018      * @return Field
43019      */
43020     findField : function(id){
43021         var field = this.items.get(id);
43022         if(!field){
43023             this.items.each(function(f){
43024                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43025                     field = f;
43026                     return false;
43027                 }
43028             });
43029         }
43030         return field || null;
43031     },
43032
43033     /**
43034      * Add a secondary form to this one, 
43035      * Used to provide tabbed forms. One form is primary, with hidden values 
43036      * which mirror the elements from the other forms.
43037      * 
43038      * @param {Roo.form.Form} form to add.
43039      * 
43040      */
43041     addForm : function(form)
43042     {
43043        
43044         if (this.childForms.indexOf(form) > -1) {
43045             // already added..
43046             return;
43047         }
43048         this.childForms.push(form);
43049         var n = '';
43050         Roo.each(form.allItems, function (fe) {
43051             
43052             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
43053             if (this.findField(n)) { // already added..
43054                 return;
43055             }
43056             var add = new Roo.form.Hidden({
43057                 name : n
43058             });
43059             add.render(this.el);
43060             
43061             this.add( add );
43062         }, this);
43063         
43064     },
43065     /**
43066      * Mark fields in this form invalid in bulk.
43067      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
43068      * @return {BasicForm} this
43069      */
43070     markInvalid : function(errors){
43071         if(errors instanceof Array){
43072             for(var i = 0, len = errors.length; i < len; i++){
43073                 var fieldError = errors[i];
43074                 var f = this.findField(fieldError.id);
43075                 if(f){
43076                     f.markInvalid(fieldError.msg);
43077                 }
43078             }
43079         }else{
43080             var field, id;
43081             for(id in errors){
43082                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
43083                     field.markInvalid(errors[id]);
43084                 }
43085             }
43086         }
43087         Roo.each(this.childForms || [], function (f) {
43088             f.markInvalid(errors);
43089         });
43090         
43091         return this;
43092     },
43093
43094     /**
43095      * Set values for fields in this form in bulk.
43096      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
43097      * @return {BasicForm} this
43098      */
43099     setValues : function(values){
43100         if(values instanceof Array){ // array of objects
43101             for(var i = 0, len = values.length; i < len; i++){
43102                 var v = values[i];
43103                 var f = this.findField(v.id);
43104                 if(f){
43105                     f.setValue(v.value);
43106                     if(this.trackResetOnLoad){
43107                         f.originalValue = f.getValue();
43108                     }
43109                 }
43110             }
43111         }else{ // object hash
43112             var field, id;
43113             for(id in values){
43114                 if(typeof values[id] != 'function' && (field = this.findField(id))){
43115                     
43116                     if (field.setFromData && 
43117                         field.valueField && 
43118                         field.displayField &&
43119                         // combos' with local stores can 
43120                         // be queried via setValue()
43121                         // to set their value..
43122                         (field.store && !field.store.isLocal)
43123                         ) {
43124                         // it's a combo
43125                         var sd = { };
43126                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
43127                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
43128                         field.setFromData(sd);
43129                         
43130                     } else {
43131                         field.setValue(values[id]);
43132                     }
43133                     
43134                     
43135                     if(this.trackResetOnLoad){
43136                         field.originalValue = field.getValue();
43137                     }
43138                 }
43139             }
43140         }
43141          
43142         Roo.each(this.childForms || [], function (f) {
43143             f.setValues(values);
43144         });
43145                 
43146         return this;
43147     },
43148
43149     /**
43150      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
43151      * they are returned as an array.
43152      * @param {Boolean} asString
43153      * @return {Object}
43154      */
43155     getValues : function(asString){
43156         if (this.childForms) {
43157             // copy values from the child forms
43158             Roo.each(this.childForms, function (f) {
43159                 this.setValues(f.getValues());
43160             }, this);
43161         }
43162         
43163         
43164         
43165         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
43166         if(asString === true){
43167             return fs;
43168         }
43169         return Roo.urlDecode(fs);
43170     },
43171     
43172     /**
43173      * Returns the fields in this form as an object with key/value pairs. 
43174      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
43175      * @return {Object}
43176      */
43177     getFieldValues : function(with_hidden)
43178     {
43179         if (this.childForms) {
43180             // copy values from the child forms
43181             // should this call getFieldValues - probably not as we do not currently copy
43182             // hidden fields when we generate..
43183             Roo.each(this.childForms, function (f) {
43184                 this.setValues(f.getValues());
43185             }, this);
43186         }
43187         
43188         var ret = {};
43189         this.items.each(function(f){
43190             if (!f.getName()) {
43191                 return;
43192             }
43193             var v = f.getValue();
43194             // not sure if this supported any more..
43195             if ((typeof(v) == 'object') && f.getRawValue) {
43196                 v = f.getRawValue() ; // dates..
43197             }
43198             // combo boxes where name != hiddenName...
43199             if (f.name != f.getName()) {
43200                 ret[f.name] = f.getRawValue();
43201             }
43202             ret[f.getName()] = v;
43203         });
43204         
43205         return ret;
43206     },
43207
43208     /**
43209      * Clears all invalid messages in this form.
43210      * @return {BasicForm} this
43211      */
43212     clearInvalid : function(){
43213         this.items.each(function(f){
43214            f.clearInvalid();
43215         });
43216         
43217         Roo.each(this.childForms || [], function (f) {
43218             f.clearInvalid();
43219         });
43220         
43221         
43222         return this;
43223     },
43224
43225     /**
43226      * Resets this form.
43227      * @return {BasicForm} this
43228      */
43229     reset : function(){
43230         this.items.each(function(f){
43231             f.reset();
43232         });
43233         
43234         Roo.each(this.childForms || [], function (f) {
43235             f.reset();
43236         });
43237        
43238         
43239         return this;
43240     },
43241
43242     /**
43243      * Add Roo.form components to this form.
43244      * @param {Field} field1
43245      * @param {Field} field2 (optional)
43246      * @param {Field} etc (optional)
43247      * @return {BasicForm} this
43248      */
43249     add : function(){
43250         this.items.addAll(Array.prototype.slice.call(arguments, 0));
43251         return this;
43252     },
43253
43254
43255     /**
43256      * Removes a field from the items collection (does NOT remove its markup).
43257      * @param {Field} field
43258      * @return {BasicForm} this
43259      */
43260     remove : function(field){
43261         this.items.remove(field);
43262         return this;
43263     },
43264
43265     /**
43266      * Looks at the fields in this form, checks them for an id attribute,
43267      * and calls applyTo on the existing dom element with that id.
43268      * @return {BasicForm} this
43269      */
43270     render : function(){
43271         this.items.each(function(f){
43272             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
43273                 f.applyTo(f.id);
43274             }
43275         });
43276         return this;
43277     },
43278
43279     /**
43280      * Calls {@link Ext#apply} for all fields in this form with the passed object.
43281      * @param {Object} values
43282      * @return {BasicForm} this
43283      */
43284     applyToFields : function(o){
43285         this.items.each(function(f){
43286            Roo.apply(f, o);
43287         });
43288         return this;
43289     },
43290
43291     /**
43292      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
43293      * @param {Object} values
43294      * @return {BasicForm} this
43295      */
43296     applyIfToFields : function(o){
43297         this.items.each(function(f){
43298            Roo.applyIf(f, o);
43299         });
43300         return this;
43301     }
43302 });
43303
43304 // back compat
43305 Roo.BasicForm = Roo.form.BasicForm;/*
43306  * Based on:
43307  * Ext JS Library 1.1.1
43308  * Copyright(c) 2006-2007, Ext JS, LLC.
43309  *
43310  * Originally Released Under LGPL - original licence link has changed is not relivant.
43311  *
43312  * Fork - LGPL
43313  * <script type="text/javascript">
43314  */
43315
43316 /**
43317  * @class Roo.form.Form
43318  * @extends Roo.form.BasicForm
43319  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
43320  * @constructor
43321  * @param {Object} config Configuration options
43322  */
43323 Roo.form.Form = function(config){
43324     var xitems =  [];
43325     if (config.items) {
43326         xitems = config.items;
43327         delete config.items;
43328     }
43329    
43330     
43331     Roo.form.Form.superclass.constructor.call(this, null, config);
43332     this.url = this.url || this.action;
43333     if(!this.root){
43334         this.root = new Roo.form.Layout(Roo.applyIf({
43335             id: Roo.id()
43336         }, config));
43337     }
43338     this.active = this.root;
43339     /**
43340      * Array of all the buttons that have been added to this form via {@link addButton}
43341      * @type Array
43342      */
43343     this.buttons = [];
43344     this.allItems = [];
43345     this.addEvents({
43346         /**
43347          * @event clientvalidation
43348          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
43349          * @param {Form} this
43350          * @param {Boolean} valid true if the form has passed client-side validation
43351          */
43352         clientvalidation: true,
43353         /**
43354          * @event rendered
43355          * Fires when the form is rendered
43356          * @param {Roo.form.Form} form
43357          */
43358         rendered : true
43359     });
43360     
43361     if (this.progressUrl) {
43362             // push a hidden field onto the list of fields..
43363             this.addxtype( {
43364                     xns: Roo.form, 
43365                     xtype : 'Hidden', 
43366                     name : 'UPLOAD_IDENTIFIER' 
43367             });
43368         }
43369         
43370     
43371     Roo.each(xitems, this.addxtype, this);
43372     
43373     
43374     
43375 };
43376
43377 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
43378     /**
43379      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
43380      */
43381     /**
43382      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
43383      */
43384     /**
43385      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
43386      */
43387     buttonAlign:'center',
43388
43389     /**
43390      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
43391      */
43392     minButtonWidth:75,
43393
43394     /**
43395      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
43396      * This property cascades to child containers if not set.
43397      */
43398     labelAlign:'left',
43399
43400     /**
43401      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
43402      * fires a looping event with that state. This is required to bind buttons to the valid
43403      * state using the config value formBind:true on the button.
43404      */
43405     monitorValid : false,
43406
43407     /**
43408      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
43409      */
43410     monitorPoll : 200,
43411     
43412     /**
43413      * @cfg {String} progressUrl - Url to return progress data 
43414      */
43415     
43416     progressUrl : false,
43417   
43418     /**
43419      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
43420      * fields are added and the column is closed. If no fields are passed the column remains open
43421      * until end() is called.
43422      * @param {Object} config The config to pass to the column
43423      * @param {Field} field1 (optional)
43424      * @param {Field} field2 (optional)
43425      * @param {Field} etc (optional)
43426      * @return Column The column container object
43427      */
43428     column : function(c){
43429         var col = new Roo.form.Column(c);
43430         this.start(col);
43431         if(arguments.length > 1){ // duplicate code required because of Opera
43432             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43433             this.end();
43434         }
43435         return col;
43436     },
43437
43438     /**
43439      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
43440      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
43441      * until end() is called.
43442      * @param {Object} config The config to pass to the fieldset
43443      * @param {Field} field1 (optional)
43444      * @param {Field} field2 (optional)
43445      * @param {Field} etc (optional)
43446      * @return FieldSet The fieldset container object
43447      */
43448     fieldset : function(c){
43449         var fs = new Roo.form.FieldSet(c);
43450         this.start(fs);
43451         if(arguments.length > 1){ // duplicate code required because of Opera
43452             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43453             this.end();
43454         }
43455         return fs;
43456     },
43457
43458     /**
43459      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
43460      * fields are added and the container is closed. If no fields are passed the container remains open
43461      * until end() is called.
43462      * @param {Object} config The config to pass to the Layout
43463      * @param {Field} field1 (optional)
43464      * @param {Field} field2 (optional)
43465      * @param {Field} etc (optional)
43466      * @return Layout The container object
43467      */
43468     container : function(c){
43469         var l = new Roo.form.Layout(c);
43470         this.start(l);
43471         if(arguments.length > 1){ // duplicate code required because of Opera
43472             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43473             this.end();
43474         }
43475         return l;
43476     },
43477
43478     /**
43479      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
43480      * @param {Object} container A Roo.form.Layout or subclass of Layout
43481      * @return {Form} this
43482      */
43483     start : function(c){
43484         // cascade label info
43485         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
43486         this.active.stack.push(c);
43487         c.ownerCt = this.active;
43488         this.active = c;
43489         return this;
43490     },
43491
43492     /**
43493      * Closes the current open container
43494      * @return {Form} this
43495      */
43496     end : function(){
43497         if(this.active == this.root){
43498             return this;
43499         }
43500         this.active = this.active.ownerCt;
43501         return this;
43502     },
43503
43504     /**
43505      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
43506      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
43507      * as the label of the field.
43508      * @param {Field} field1
43509      * @param {Field} field2 (optional)
43510      * @param {Field} etc. (optional)
43511      * @return {Form} this
43512      */
43513     add : function(){
43514         this.active.stack.push.apply(this.active.stack, arguments);
43515         this.allItems.push.apply(this.allItems,arguments);
43516         var r = [];
43517         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
43518             if(a[i].isFormField){
43519                 r.push(a[i]);
43520             }
43521         }
43522         if(r.length > 0){
43523             Roo.form.Form.superclass.add.apply(this, r);
43524         }
43525         return this;
43526     },
43527     
43528
43529     
43530     
43531     
43532      /**
43533      * Find any element that has been added to a form, using it's ID or name
43534      * This can include framesets, columns etc. along with regular fields..
43535      * @param {String} id - id or name to find.
43536      
43537      * @return {Element} e - or false if nothing found.
43538      */
43539     findbyId : function(id)
43540     {
43541         var ret = false;
43542         if (!id) {
43543             return ret;
43544         }
43545         Roo.each(this.allItems, function(f){
43546             if (f.id == id || f.name == id ){
43547                 ret = f;
43548                 return false;
43549             }
43550         });
43551         return ret;
43552     },
43553
43554     
43555     
43556     /**
43557      * Render this form into the passed container. This should only be called once!
43558      * @param {String/HTMLElement/Element} container The element this component should be rendered into
43559      * @return {Form} this
43560      */
43561     render : function(ct)
43562     {
43563         
43564         
43565         
43566         ct = Roo.get(ct);
43567         var o = this.autoCreate || {
43568             tag: 'form',
43569             method : this.method || 'POST',
43570             id : this.id || Roo.id()
43571         };
43572         this.initEl(ct.createChild(o));
43573
43574         this.root.render(this.el);
43575         
43576        
43577              
43578         this.items.each(function(f){
43579             f.render('x-form-el-'+f.id);
43580         });
43581
43582         if(this.buttons.length > 0){
43583             // tables are required to maintain order and for correct IE layout
43584             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
43585                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
43586                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
43587             }}, null, true);
43588             var tr = tb.getElementsByTagName('tr')[0];
43589             for(var i = 0, len = this.buttons.length; i < len; i++) {
43590                 var b = this.buttons[i];
43591                 var td = document.createElement('td');
43592                 td.className = 'x-form-btn-td';
43593                 b.render(tr.appendChild(td));
43594             }
43595         }
43596         if(this.monitorValid){ // initialize after render
43597             this.startMonitoring();
43598         }
43599         this.fireEvent('rendered', this);
43600         return this;
43601     },
43602
43603     /**
43604      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
43605      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
43606      * object or a valid Roo.DomHelper element config
43607      * @param {Function} handler The function called when the button is clicked
43608      * @param {Object} scope (optional) The scope of the handler function
43609      * @return {Roo.Button}
43610      */
43611     addButton : function(config, handler, scope){
43612         var bc = {
43613             handler: handler,
43614             scope: scope,
43615             minWidth: this.minButtonWidth,
43616             hideParent:true
43617         };
43618         if(typeof config == "string"){
43619             bc.text = config;
43620         }else{
43621             Roo.apply(bc, config);
43622         }
43623         var btn = new Roo.Button(null, bc);
43624         this.buttons.push(btn);
43625         return btn;
43626     },
43627
43628      /**
43629      * Adds a series of form elements (using the xtype property as the factory method.
43630      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
43631      * @param {Object} config 
43632      */
43633     
43634     addxtype : function()
43635     {
43636         var ar = Array.prototype.slice.call(arguments, 0);
43637         var ret = false;
43638         for(var i = 0; i < ar.length; i++) {
43639             if (!ar[i]) {
43640                 continue; // skip -- if this happends something invalid got sent, we 
43641                 // should ignore it, as basically that interface element will not show up
43642                 // and that should be pretty obvious!!
43643             }
43644             
43645             if (Roo.form[ar[i].xtype]) {
43646                 ar[i].form = this;
43647                 var fe = Roo.factory(ar[i], Roo.form);
43648                 if (!ret) {
43649                     ret = fe;
43650                 }
43651                 fe.form = this;
43652                 if (fe.store) {
43653                     fe.store.form = this;
43654                 }
43655                 if (fe.isLayout) {  
43656                          
43657                     this.start(fe);
43658                     this.allItems.push(fe);
43659                     if (fe.items && fe.addxtype) {
43660                         fe.addxtype.apply(fe, fe.items);
43661                         delete fe.items;
43662                     }
43663                      this.end();
43664                     continue;
43665                 }
43666                 
43667                 
43668                  
43669                 this.add(fe);
43670               //  console.log('adding ' + ar[i].xtype);
43671             }
43672             if (ar[i].xtype == 'Button') {  
43673                 //console.log('adding button');
43674                 //console.log(ar[i]);
43675                 this.addButton(ar[i]);
43676                 this.allItems.push(fe);
43677                 continue;
43678             }
43679             
43680             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
43681                 alert('end is not supported on xtype any more, use items');
43682             //    this.end();
43683             //    //console.log('adding end');
43684             }
43685             
43686         }
43687         return ret;
43688     },
43689     
43690     /**
43691      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
43692      * option "monitorValid"
43693      */
43694     startMonitoring : function(){
43695         if(!this.bound){
43696             this.bound = true;
43697             Roo.TaskMgr.start({
43698                 run : this.bindHandler,
43699                 interval : this.monitorPoll || 200,
43700                 scope: this
43701             });
43702         }
43703     },
43704
43705     /**
43706      * Stops monitoring of the valid state of this form
43707      */
43708     stopMonitoring : function(){
43709         this.bound = false;
43710     },
43711
43712     // private
43713     bindHandler : function(){
43714         if(!this.bound){
43715             return false; // stops binding
43716         }
43717         var valid = true;
43718         this.items.each(function(f){
43719             if(!f.isValid(true)){
43720                 valid = false;
43721                 return false;
43722             }
43723         });
43724         for(var i = 0, len = this.buttons.length; i < len; i++){
43725             var btn = this.buttons[i];
43726             if(btn.formBind === true && btn.disabled === valid){
43727                 btn.setDisabled(!valid);
43728             }
43729         }
43730         this.fireEvent('clientvalidation', this, valid);
43731     }
43732     
43733     
43734     
43735     
43736     
43737     
43738     
43739     
43740 });
43741
43742
43743 // back compat
43744 Roo.Form = Roo.form.Form;
43745 /*
43746  * Based on:
43747  * Ext JS Library 1.1.1
43748  * Copyright(c) 2006-2007, Ext JS, LLC.
43749  *
43750  * Originally Released Under LGPL - original licence link has changed is not relivant.
43751  *
43752  * Fork - LGPL
43753  * <script type="text/javascript">
43754  */
43755  
43756  /**
43757  * @class Roo.form.Action
43758  * Internal Class used to handle form actions
43759  * @constructor
43760  * @param {Roo.form.BasicForm} el The form element or its id
43761  * @param {Object} config Configuration options
43762  */
43763  
43764  
43765 // define the action interface
43766 Roo.form.Action = function(form, options){
43767     this.form = form;
43768     this.options = options || {};
43769 };
43770 /**
43771  * Client Validation Failed
43772  * @const 
43773  */
43774 Roo.form.Action.CLIENT_INVALID = 'client';
43775 /**
43776  * Server Validation Failed
43777  * @const 
43778  */
43779  Roo.form.Action.SERVER_INVALID = 'server';
43780  /**
43781  * Connect to Server Failed
43782  * @const 
43783  */
43784 Roo.form.Action.CONNECT_FAILURE = 'connect';
43785 /**
43786  * Reading Data from Server Failed
43787  * @const 
43788  */
43789 Roo.form.Action.LOAD_FAILURE = 'load';
43790
43791 Roo.form.Action.prototype = {
43792     type : 'default',
43793     failureType : undefined,
43794     response : undefined,
43795     result : undefined,
43796
43797     // interface method
43798     run : function(options){
43799
43800     },
43801
43802     // interface method
43803     success : function(response){
43804
43805     },
43806
43807     // interface method
43808     handleResponse : function(response){
43809
43810     },
43811
43812     // default connection failure
43813     failure : function(response){
43814         
43815         this.response = response;
43816         this.failureType = Roo.form.Action.CONNECT_FAILURE;
43817         this.form.afterAction(this, false);
43818     },
43819
43820     processResponse : function(response){
43821         this.response = response;
43822         if(!response.responseText){
43823             return true;
43824         }
43825         this.result = this.handleResponse(response);
43826         return this.result;
43827     },
43828
43829     // utility functions used internally
43830     getUrl : function(appendParams){
43831         var url = this.options.url || this.form.url || this.form.el.dom.action;
43832         if(appendParams){
43833             var p = this.getParams();
43834             if(p){
43835                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
43836             }
43837         }
43838         return url;
43839     },
43840
43841     getMethod : function(){
43842         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
43843     },
43844
43845     getParams : function(){
43846         var bp = this.form.baseParams;
43847         var p = this.options.params;
43848         if(p){
43849             if(typeof p == "object"){
43850                 p = Roo.urlEncode(Roo.applyIf(p, bp));
43851             }else if(typeof p == 'string' && bp){
43852                 p += '&' + Roo.urlEncode(bp);
43853             }
43854         }else if(bp){
43855             p = Roo.urlEncode(bp);
43856         }
43857         return p;
43858     },
43859
43860     createCallback : function(){
43861         return {
43862             success: this.success,
43863             failure: this.failure,
43864             scope: this,
43865             timeout: (this.form.timeout*1000),
43866             upload: this.form.fileUpload ? this.success : undefined
43867         };
43868     }
43869 };
43870
43871 Roo.form.Action.Submit = function(form, options){
43872     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
43873 };
43874
43875 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
43876     type : 'submit',
43877
43878     haveProgress : false,
43879     uploadComplete : false,
43880     
43881     // uploadProgress indicator.
43882     uploadProgress : function()
43883     {
43884         if (!this.form.progressUrl) {
43885             return;
43886         }
43887         
43888         if (!this.haveProgress) {
43889             Roo.MessageBox.progress("Uploading", "Uploading");
43890         }
43891         if (this.uploadComplete) {
43892            Roo.MessageBox.hide();
43893            return;
43894         }
43895         
43896         this.haveProgress = true;
43897    
43898         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
43899         
43900         var c = new Roo.data.Connection();
43901         c.request({
43902             url : this.form.progressUrl,
43903             params: {
43904                 id : uid
43905             },
43906             method: 'GET',
43907             success : function(req){
43908                //console.log(data);
43909                 var rdata = false;
43910                 var edata;
43911                 try  {
43912                    rdata = Roo.decode(req.responseText)
43913                 } catch (e) {
43914                     Roo.log("Invalid data from server..");
43915                     Roo.log(edata);
43916                     return;
43917                 }
43918                 if (!rdata || !rdata.success) {
43919                     Roo.log(rdata);
43920                     Roo.MessageBox.alert(Roo.encode(rdata));
43921                     return;
43922                 }
43923                 var data = rdata.data;
43924                 
43925                 if (this.uploadComplete) {
43926                    Roo.MessageBox.hide();
43927                    return;
43928                 }
43929                    
43930                 if (data){
43931                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
43932                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
43933                     );
43934                 }
43935                 this.uploadProgress.defer(2000,this);
43936             },
43937        
43938             failure: function(data) {
43939                 Roo.log('progress url failed ');
43940                 Roo.log(data);
43941             },
43942             scope : this
43943         });
43944            
43945     },
43946     
43947     
43948     run : function()
43949     {
43950         // run get Values on the form, so it syncs any secondary forms.
43951         this.form.getValues();
43952         
43953         var o = this.options;
43954         var method = this.getMethod();
43955         var isPost = method == 'POST';
43956         if(o.clientValidation === false || this.form.isValid()){
43957             
43958             if (this.form.progressUrl) {
43959                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
43960                     (new Date() * 1) + '' + Math.random());
43961                     
43962             } 
43963             
43964             
43965             Roo.Ajax.request(Roo.apply(this.createCallback(), {
43966                 form:this.form.el.dom,
43967                 url:this.getUrl(!isPost),
43968                 method: method,
43969                 params:isPost ? this.getParams() : null,
43970                 isUpload: this.form.fileUpload
43971             }));
43972             
43973             this.uploadProgress();
43974
43975         }else if (o.clientValidation !== false){ // client validation failed
43976             this.failureType = Roo.form.Action.CLIENT_INVALID;
43977             this.form.afterAction(this, false);
43978         }
43979     },
43980
43981     success : function(response)
43982     {
43983         this.uploadComplete= true;
43984         if (this.haveProgress) {
43985             Roo.MessageBox.hide();
43986         }
43987         
43988         
43989         var result = this.processResponse(response);
43990         if(result === true || result.success){
43991             this.form.afterAction(this, true);
43992             return;
43993         }
43994         if(result.errors){
43995             this.form.markInvalid(result.errors);
43996             this.failureType = Roo.form.Action.SERVER_INVALID;
43997         }
43998         this.form.afterAction(this, false);
43999     },
44000     failure : function(response)
44001     {
44002         this.uploadComplete= true;
44003         if (this.haveProgress) {
44004             Roo.MessageBox.hide();
44005         }
44006         
44007         this.response = response;
44008         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44009         this.form.afterAction(this, false);
44010     },
44011     
44012     handleResponse : function(response){
44013         if(this.form.errorReader){
44014             var rs = this.form.errorReader.read(response);
44015             var errors = [];
44016             if(rs.records){
44017                 for(var i = 0, len = rs.records.length; i < len; i++) {
44018                     var r = rs.records[i];
44019                     errors[i] = r.data;
44020                 }
44021             }
44022             if(errors.length < 1){
44023                 errors = null;
44024             }
44025             return {
44026                 success : rs.success,
44027                 errors : errors
44028             };
44029         }
44030         var ret = false;
44031         try {
44032             ret = Roo.decode(response.responseText);
44033         } catch (e) {
44034             ret = {
44035                 success: false,
44036                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
44037                 errors : []
44038             };
44039         }
44040         return ret;
44041         
44042     }
44043 });
44044
44045
44046 Roo.form.Action.Load = function(form, options){
44047     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
44048     this.reader = this.form.reader;
44049 };
44050
44051 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
44052     type : 'load',
44053
44054     run : function(){
44055         
44056         Roo.Ajax.request(Roo.apply(
44057                 this.createCallback(), {
44058                     method:this.getMethod(),
44059                     url:this.getUrl(false),
44060                     params:this.getParams()
44061         }));
44062     },
44063
44064     success : function(response){
44065         
44066         var result = this.processResponse(response);
44067         if(result === true || !result.success || !result.data){
44068             this.failureType = Roo.form.Action.LOAD_FAILURE;
44069             this.form.afterAction(this, false);
44070             return;
44071         }
44072         this.form.clearInvalid();
44073         this.form.setValues(result.data);
44074         this.form.afterAction(this, true);
44075     },
44076
44077     handleResponse : function(response){
44078         if(this.form.reader){
44079             var rs = this.form.reader.read(response);
44080             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
44081             return {
44082                 success : rs.success,
44083                 data : data
44084             };
44085         }
44086         return Roo.decode(response.responseText);
44087     }
44088 });
44089
44090 Roo.form.Action.ACTION_TYPES = {
44091     'load' : Roo.form.Action.Load,
44092     'submit' : Roo.form.Action.Submit
44093 };/*
44094  * Based on:
44095  * Ext JS Library 1.1.1
44096  * Copyright(c) 2006-2007, Ext JS, LLC.
44097  *
44098  * Originally Released Under LGPL - original licence link has changed is not relivant.
44099  *
44100  * Fork - LGPL
44101  * <script type="text/javascript">
44102  */
44103  
44104 /**
44105  * @class Roo.form.Layout
44106  * @extends Roo.Component
44107  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
44108  * @constructor
44109  * @param {Object} config Configuration options
44110  */
44111 Roo.form.Layout = function(config){
44112     var xitems = [];
44113     if (config.items) {
44114         xitems = config.items;
44115         delete config.items;
44116     }
44117     Roo.form.Layout.superclass.constructor.call(this, config);
44118     this.stack = [];
44119     Roo.each(xitems, this.addxtype, this);
44120      
44121 };
44122
44123 Roo.extend(Roo.form.Layout, Roo.Component, {
44124     /**
44125      * @cfg {String/Object} autoCreate
44126      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
44127      */
44128     /**
44129      * @cfg {String/Object/Function} style
44130      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
44131      * a function which returns such a specification.
44132      */
44133     /**
44134      * @cfg {String} labelAlign
44135      * Valid values are "left," "top" and "right" (defaults to "left")
44136      */
44137     /**
44138      * @cfg {Number} labelWidth
44139      * Fixed width in pixels of all field labels (defaults to undefined)
44140      */
44141     /**
44142      * @cfg {Boolean} clear
44143      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
44144      */
44145     clear : true,
44146     /**
44147      * @cfg {String} labelSeparator
44148      * The separator to use after field labels (defaults to ':')
44149      */
44150     labelSeparator : ':',
44151     /**
44152      * @cfg {Boolean} hideLabels
44153      * True to suppress the display of field labels in this layout (defaults to false)
44154      */
44155     hideLabels : false,
44156
44157     // private
44158     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
44159     
44160     isLayout : true,
44161     
44162     // private
44163     onRender : function(ct, position){
44164         if(this.el){ // from markup
44165             this.el = Roo.get(this.el);
44166         }else {  // generate
44167             var cfg = this.getAutoCreate();
44168             this.el = ct.createChild(cfg, position);
44169         }
44170         if(this.style){
44171             this.el.applyStyles(this.style);
44172         }
44173         if(this.labelAlign){
44174             this.el.addClass('x-form-label-'+this.labelAlign);
44175         }
44176         if(this.hideLabels){
44177             this.labelStyle = "display:none";
44178             this.elementStyle = "padding-left:0;";
44179         }else{
44180             if(typeof this.labelWidth == 'number'){
44181                 this.labelStyle = "width:"+this.labelWidth+"px;";
44182                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
44183             }
44184             if(this.labelAlign == 'top'){
44185                 this.labelStyle = "width:auto;";
44186                 this.elementStyle = "padding-left:0;";
44187             }
44188         }
44189         var stack = this.stack;
44190         var slen = stack.length;
44191         if(slen > 0){
44192             if(!this.fieldTpl){
44193                 var t = new Roo.Template(
44194                     '<div class="x-form-item {5}">',
44195                         '<label for="{0}" style="{2}">{1}{4}</label>',
44196                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44197                         '</div>',
44198                     '</div><div class="x-form-clear-left"></div>'
44199                 );
44200                 t.disableFormats = true;
44201                 t.compile();
44202                 Roo.form.Layout.prototype.fieldTpl = t;
44203             }
44204             for(var i = 0; i < slen; i++) {
44205                 if(stack[i].isFormField){
44206                     this.renderField(stack[i]);
44207                 }else{
44208                     this.renderComponent(stack[i]);
44209                 }
44210             }
44211         }
44212         if(this.clear){
44213             this.el.createChild({cls:'x-form-clear'});
44214         }
44215     },
44216
44217     // private
44218     renderField : function(f){
44219         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
44220                f.id, //0
44221                f.fieldLabel, //1
44222                f.labelStyle||this.labelStyle||'', //2
44223                this.elementStyle||'', //3
44224                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
44225                f.itemCls||this.itemCls||''  //5
44226        ], true).getPrevSibling());
44227     },
44228
44229     // private
44230     renderComponent : function(c){
44231         c.render(c.isLayout ? this.el : this.el.createChild());    
44232     },
44233     /**
44234      * Adds a object form elements (using the xtype property as the factory method.)
44235      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
44236      * @param {Object} config 
44237      */
44238     addxtype : function(o)
44239     {
44240         // create the lement.
44241         o.form = this.form;
44242         var fe = Roo.factory(o, Roo.form);
44243         this.form.allItems.push(fe);
44244         this.stack.push(fe);
44245         
44246         if (fe.isFormField) {
44247             this.form.items.add(fe);
44248         }
44249          
44250         return fe;
44251     }
44252 });
44253
44254 /**
44255  * @class Roo.form.Column
44256  * @extends Roo.form.Layout
44257  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
44258  * @constructor
44259  * @param {Object} config Configuration options
44260  */
44261 Roo.form.Column = function(config){
44262     Roo.form.Column.superclass.constructor.call(this, config);
44263 };
44264
44265 Roo.extend(Roo.form.Column, Roo.form.Layout, {
44266     /**
44267      * @cfg {Number/String} width
44268      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44269      */
44270     /**
44271      * @cfg {String/Object} autoCreate
44272      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
44273      */
44274
44275     // private
44276     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
44277
44278     // private
44279     onRender : function(ct, position){
44280         Roo.form.Column.superclass.onRender.call(this, ct, position);
44281         if(this.width){
44282             this.el.setWidth(this.width);
44283         }
44284     }
44285 });
44286
44287
44288 /**
44289  * @class Roo.form.Row
44290  * @extends Roo.form.Layout
44291  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
44292  * @constructor
44293  * @param {Object} config Configuration options
44294  */
44295
44296  
44297 Roo.form.Row = function(config){
44298     Roo.form.Row.superclass.constructor.call(this, config);
44299 };
44300  
44301 Roo.extend(Roo.form.Row, Roo.form.Layout, {
44302       /**
44303      * @cfg {Number/String} width
44304      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44305      */
44306     /**
44307      * @cfg {Number/String} height
44308      * The fixed height of the column in pixels or CSS value (defaults to "auto")
44309      */
44310     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
44311     
44312     padWidth : 20,
44313     // private
44314     onRender : function(ct, position){
44315         //console.log('row render');
44316         if(!this.rowTpl){
44317             var t = new Roo.Template(
44318                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
44319                     '<label for="{0}" style="{2}">{1}{4}</label>',
44320                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44321                     '</div>',
44322                 '</div>'
44323             );
44324             t.disableFormats = true;
44325             t.compile();
44326             Roo.form.Layout.prototype.rowTpl = t;
44327         }
44328         this.fieldTpl = this.rowTpl;
44329         
44330         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
44331         var labelWidth = 100;
44332         
44333         if ((this.labelAlign != 'top')) {
44334             if (typeof this.labelWidth == 'number') {
44335                 labelWidth = this.labelWidth
44336             }
44337             this.padWidth =  20 + labelWidth;
44338             
44339         }
44340         
44341         Roo.form.Column.superclass.onRender.call(this, ct, position);
44342         if(this.width){
44343             this.el.setWidth(this.width);
44344         }
44345         if(this.height){
44346             this.el.setHeight(this.height);
44347         }
44348     },
44349     
44350     // private
44351     renderField : function(f){
44352         f.fieldEl = this.fieldTpl.append(this.el, [
44353                f.id, f.fieldLabel,
44354                f.labelStyle||this.labelStyle||'',
44355                this.elementStyle||'',
44356                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
44357                f.itemCls||this.itemCls||'',
44358                f.width ? f.width + this.padWidth : 160 + this.padWidth
44359        ],true);
44360     }
44361 });
44362  
44363
44364 /**
44365  * @class Roo.form.FieldSet
44366  * @extends Roo.form.Layout
44367  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
44368  * @constructor
44369  * @param {Object} config Configuration options
44370  */
44371 Roo.form.FieldSet = function(config){
44372     Roo.form.FieldSet.superclass.constructor.call(this, config);
44373 };
44374
44375 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
44376     /**
44377      * @cfg {String} legend
44378      * The text to display as the legend for the FieldSet (defaults to '')
44379      */
44380     /**
44381      * @cfg {String/Object} autoCreate
44382      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
44383      */
44384
44385     // private
44386     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
44387
44388     // private
44389     onRender : function(ct, position){
44390         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
44391         if(this.legend){
44392             this.setLegend(this.legend);
44393         }
44394     },
44395
44396     // private
44397     setLegend : function(text){
44398         if(this.rendered){
44399             this.el.child('legend').update(text);
44400         }
44401     }
44402 });/*
44403  * Based on:
44404  * Ext JS Library 1.1.1
44405  * Copyright(c) 2006-2007, Ext JS, LLC.
44406  *
44407  * Originally Released Under LGPL - original licence link has changed is not relivant.
44408  *
44409  * Fork - LGPL
44410  * <script type="text/javascript">
44411  */
44412 /**
44413  * @class Roo.form.VTypes
44414  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
44415  * @singleton
44416  */
44417 Roo.form.VTypes = function(){
44418     // closure these in so they are only created once.
44419     var alpha = /^[a-zA-Z_]+$/;
44420     var alphanum = /^[a-zA-Z0-9_]+$/;
44421     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
44422     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
44423
44424     // All these messages and functions are configurable
44425     return {
44426         /**
44427          * The function used to validate email addresses
44428          * @param {String} value The email address
44429          */
44430         'email' : function(v){
44431             return email.test(v);
44432         },
44433         /**
44434          * The error text to display when the email validation function returns false
44435          * @type String
44436          */
44437         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
44438         /**
44439          * The keystroke filter mask to be applied on email input
44440          * @type RegExp
44441          */
44442         'emailMask' : /[a-z0-9_\.\-@]/i,
44443
44444         /**
44445          * The function used to validate URLs
44446          * @param {String} value The URL
44447          */
44448         'url' : function(v){
44449             return url.test(v);
44450         },
44451         /**
44452          * The error text to display when the url validation function returns false
44453          * @type String
44454          */
44455         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
44456         
44457         /**
44458          * The function used to validate alpha values
44459          * @param {String} value The value
44460          */
44461         'alpha' : function(v){
44462             return alpha.test(v);
44463         },
44464         /**
44465          * The error text to display when the alpha validation function returns false
44466          * @type String
44467          */
44468         'alphaText' : 'This field should only contain letters and _',
44469         /**
44470          * The keystroke filter mask to be applied on alpha input
44471          * @type RegExp
44472          */
44473         'alphaMask' : /[a-z_]/i,
44474
44475         /**
44476          * The function used to validate alphanumeric values
44477          * @param {String} value The value
44478          */
44479         'alphanum' : function(v){
44480             return alphanum.test(v);
44481         },
44482         /**
44483          * The error text to display when the alphanumeric validation function returns false
44484          * @type String
44485          */
44486         'alphanumText' : 'This field should only contain letters, numbers and _',
44487         /**
44488          * The keystroke filter mask to be applied on alphanumeric input
44489          * @type RegExp
44490          */
44491         'alphanumMask' : /[a-z0-9_]/i
44492     };
44493 }();//<script type="text/javascript">
44494
44495 /**
44496  * @class Roo.form.FCKeditor
44497  * @extends Roo.form.TextArea
44498  * Wrapper around the FCKEditor http://www.fckeditor.net
44499  * @constructor
44500  * Creates a new FCKeditor
44501  * @param {Object} config Configuration options
44502  */
44503 Roo.form.FCKeditor = function(config){
44504     Roo.form.FCKeditor.superclass.constructor.call(this, config);
44505     this.addEvents({
44506          /**
44507          * @event editorinit
44508          * Fired when the editor is initialized - you can add extra handlers here..
44509          * @param {FCKeditor} this
44510          * @param {Object} the FCK object.
44511          */
44512         editorinit : true
44513     });
44514     
44515     
44516 };
44517 Roo.form.FCKeditor.editors = { };
44518 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
44519 {
44520     //defaultAutoCreate : {
44521     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
44522     //},
44523     // private
44524     /**
44525      * @cfg {Object} fck options - see fck manual for details.
44526      */
44527     fckconfig : false,
44528     
44529     /**
44530      * @cfg {Object} fck toolbar set (Basic or Default)
44531      */
44532     toolbarSet : 'Basic',
44533     /**
44534      * @cfg {Object} fck BasePath
44535      */ 
44536     basePath : '/fckeditor/',
44537     
44538     
44539     frame : false,
44540     
44541     value : '',
44542     
44543    
44544     onRender : function(ct, position)
44545     {
44546         if(!this.el){
44547             this.defaultAutoCreate = {
44548                 tag: "textarea",
44549                 style:"width:300px;height:60px;",
44550                 autocomplete: "off"
44551             };
44552         }
44553         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
44554         /*
44555         if(this.grow){
44556             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
44557             if(this.preventScrollbars){
44558                 this.el.setStyle("overflow", "hidden");
44559             }
44560             this.el.setHeight(this.growMin);
44561         }
44562         */
44563         //console.log('onrender' + this.getId() );
44564         Roo.form.FCKeditor.editors[this.getId()] = this;
44565          
44566
44567         this.replaceTextarea() ;
44568         
44569     },
44570     
44571     getEditor : function() {
44572         return this.fckEditor;
44573     },
44574     /**
44575      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
44576      * @param {Mixed} value The value to set
44577      */
44578     
44579     
44580     setValue : function(value)
44581     {
44582         //console.log('setValue: ' + value);
44583         
44584         if(typeof(value) == 'undefined') { // not sure why this is happending...
44585             return;
44586         }
44587         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44588         
44589         //if(!this.el || !this.getEditor()) {
44590         //    this.value = value;
44591             //this.setValue.defer(100,this,[value]);    
44592         //    return;
44593         //} 
44594         
44595         if(!this.getEditor()) {
44596             return;
44597         }
44598         
44599         this.getEditor().SetData(value);
44600         
44601         //
44602
44603     },
44604
44605     /**
44606      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
44607      * @return {Mixed} value The field value
44608      */
44609     getValue : function()
44610     {
44611         
44612         if (this.frame && this.frame.dom.style.display == 'none') {
44613             return Roo.form.FCKeditor.superclass.getValue.call(this);
44614         }
44615         
44616         if(!this.el || !this.getEditor()) {
44617            
44618            // this.getValue.defer(100,this); 
44619             return this.value;
44620         }
44621        
44622         
44623         var value=this.getEditor().GetData();
44624         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44625         return Roo.form.FCKeditor.superclass.getValue.call(this);
44626         
44627
44628     },
44629
44630     /**
44631      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
44632      * @return {Mixed} value The field value
44633      */
44634     getRawValue : function()
44635     {
44636         if (this.frame && this.frame.dom.style.display == 'none') {
44637             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44638         }
44639         
44640         if(!this.el || !this.getEditor()) {
44641             //this.getRawValue.defer(100,this); 
44642             return this.value;
44643             return;
44644         }
44645         
44646         
44647         
44648         var value=this.getEditor().GetData();
44649         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
44650         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44651          
44652     },
44653     
44654     setSize : function(w,h) {
44655         
44656         
44657         
44658         //if (this.frame && this.frame.dom.style.display == 'none') {
44659         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44660         //    return;
44661         //}
44662         //if(!this.el || !this.getEditor()) {
44663         //    this.setSize.defer(100,this, [w,h]); 
44664         //    return;
44665         //}
44666         
44667         
44668         
44669         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44670         
44671         this.frame.dom.setAttribute('width', w);
44672         this.frame.dom.setAttribute('height', h);
44673         this.frame.setSize(w,h);
44674         
44675     },
44676     
44677     toggleSourceEdit : function(value) {
44678         
44679       
44680          
44681         this.el.dom.style.display = value ? '' : 'none';
44682         this.frame.dom.style.display = value ?  'none' : '';
44683         
44684     },
44685     
44686     
44687     focus: function(tag)
44688     {
44689         if (this.frame.dom.style.display == 'none') {
44690             return Roo.form.FCKeditor.superclass.focus.call(this);
44691         }
44692         if(!this.el || !this.getEditor()) {
44693             this.focus.defer(100,this, [tag]); 
44694             return;
44695         }
44696         
44697         
44698         
44699         
44700         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
44701         this.getEditor().Focus();
44702         if (tgs.length) {
44703             if (!this.getEditor().Selection.GetSelection()) {
44704                 this.focus.defer(100,this, [tag]); 
44705                 return;
44706             }
44707             
44708             
44709             var r = this.getEditor().EditorDocument.createRange();
44710             r.setStart(tgs[0],0);
44711             r.setEnd(tgs[0],0);
44712             this.getEditor().Selection.GetSelection().removeAllRanges();
44713             this.getEditor().Selection.GetSelection().addRange(r);
44714             this.getEditor().Focus();
44715         }
44716         
44717     },
44718     
44719     
44720     
44721     replaceTextarea : function()
44722     {
44723         if ( document.getElementById( this.getId() + '___Frame' ) )
44724             return ;
44725         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
44726         //{
44727             // We must check the elements firstly using the Id and then the name.
44728         var oTextarea = document.getElementById( this.getId() );
44729         
44730         var colElementsByName = document.getElementsByName( this.getId() ) ;
44731          
44732         oTextarea.style.display = 'none' ;
44733
44734         if ( oTextarea.tabIndex ) {            
44735             this.TabIndex = oTextarea.tabIndex ;
44736         }
44737         
44738         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
44739         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
44740         this.frame = Roo.get(this.getId() + '___Frame')
44741     },
44742     
44743     _getConfigHtml : function()
44744     {
44745         var sConfig = '' ;
44746
44747         for ( var o in this.fckconfig ) {
44748             sConfig += sConfig.length > 0  ? '&amp;' : '';
44749             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
44750         }
44751
44752         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
44753     },
44754     
44755     
44756     _getIFrameHtml : function()
44757     {
44758         var sFile = 'fckeditor.html' ;
44759         /* no idea what this is about..
44760         try
44761         {
44762             if ( (/fcksource=true/i).test( window.top.location.search ) )
44763                 sFile = 'fckeditor.original.html' ;
44764         }
44765         catch (e) { 
44766         */
44767
44768         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
44769         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
44770         
44771         
44772         var html = '<iframe id="' + this.getId() +
44773             '___Frame" src="' + sLink +
44774             '" width="' + this.width +
44775             '" height="' + this.height + '"' +
44776             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
44777             ' frameborder="0" scrolling="no"></iframe>' ;
44778
44779         return html ;
44780     },
44781     
44782     _insertHtmlBefore : function( html, element )
44783     {
44784         if ( element.insertAdjacentHTML )       {
44785             // IE
44786             element.insertAdjacentHTML( 'beforeBegin', html ) ;
44787         } else { // Gecko
44788             var oRange = document.createRange() ;
44789             oRange.setStartBefore( element ) ;
44790             var oFragment = oRange.createContextualFragment( html );
44791             element.parentNode.insertBefore( oFragment, element ) ;
44792         }
44793     }
44794     
44795     
44796   
44797     
44798     
44799     
44800     
44801
44802 });
44803
44804 //Roo.reg('fckeditor', Roo.form.FCKeditor);
44805
44806 function FCKeditor_OnComplete(editorInstance){
44807     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
44808     f.fckEditor = editorInstance;
44809     //console.log("loaded");
44810     f.fireEvent('editorinit', f, editorInstance);
44811
44812   
44813
44814  
44815
44816
44817
44818
44819
44820
44821
44822
44823
44824
44825
44826
44827
44828
44829
44830 //<script type="text/javascript">
44831 /**
44832  * @class Roo.form.GridField
44833  * @extends Roo.form.Field
44834  * Embed a grid (or editable grid into a form)
44835  * STATUS ALPHA
44836  * 
44837  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
44838  * it needs 
44839  * xgrid.store = Roo.data.Store
44840  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
44841  * xgrid.store.reader = Roo.data.JsonReader 
44842  * 
44843  * 
44844  * @constructor
44845  * Creates a new GridField
44846  * @param {Object} config Configuration options
44847  */
44848 Roo.form.GridField = function(config){
44849     Roo.form.GridField.superclass.constructor.call(this, config);
44850      
44851 };
44852
44853 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
44854     /**
44855      * @cfg {Number} width  - used to restrict width of grid..
44856      */
44857     width : 100,
44858     /**
44859      * @cfg {Number} height - used to restrict height of grid..
44860      */
44861     height : 50,
44862      /**
44863      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
44864          * 
44865          *}
44866      */
44867     xgrid : false, 
44868     /**
44869      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44870      * {tag: "input", type: "checkbox", autocomplete: "off"})
44871      */
44872    // defaultAutoCreate : { tag: 'div' },
44873     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
44874     /**
44875      * @cfg {String} addTitle Text to include for adding a title.
44876      */
44877     addTitle : false,
44878     //
44879     onResize : function(){
44880         Roo.form.Field.superclass.onResize.apply(this, arguments);
44881     },
44882
44883     initEvents : function(){
44884         // Roo.form.Checkbox.superclass.initEvents.call(this);
44885         // has no events...
44886        
44887     },
44888
44889
44890     getResizeEl : function(){
44891         return this.wrap;
44892     },
44893
44894     getPositionEl : function(){
44895         return this.wrap;
44896     },
44897
44898     // private
44899     onRender : function(ct, position){
44900         
44901         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
44902         var style = this.style;
44903         delete this.style;
44904         
44905         Roo.form.GridField.superclass.onRender.call(this, ct, position);
44906         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
44907         this.viewEl = this.wrap.createChild({ tag: 'div' });
44908         if (style) {
44909             this.viewEl.applyStyles(style);
44910         }
44911         if (this.width) {
44912             this.viewEl.setWidth(this.width);
44913         }
44914         if (this.height) {
44915             this.viewEl.setHeight(this.height);
44916         }
44917         //if(this.inputValue !== undefined){
44918         //this.setValue(this.value);
44919         
44920         
44921         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
44922         
44923         
44924         this.grid.render();
44925         this.grid.getDataSource().on('remove', this.refreshValue, this);
44926         this.grid.getDataSource().on('update', this.refreshValue, this);
44927         this.grid.on('afteredit', this.refreshValue, this);
44928  
44929     },
44930      
44931     
44932     /**
44933      * Sets the value of the item. 
44934      * @param {String} either an object  or a string..
44935      */
44936     setValue : function(v){
44937         //this.value = v;
44938         v = v || []; // empty set..
44939         // this does not seem smart - it really only affects memoryproxy grids..
44940         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
44941             var ds = this.grid.getDataSource();
44942             // assumes a json reader..
44943             var data = {}
44944             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
44945             ds.loadData( data);
44946         }
44947         // clear selection so it does not get stale.
44948         if (this.grid.sm) { 
44949             this.grid.sm.clearSelections();
44950         }
44951         
44952         Roo.form.GridField.superclass.setValue.call(this, v);
44953         this.refreshValue();
44954         // should load data in the grid really....
44955     },
44956     
44957     // private
44958     refreshValue: function() {
44959          var val = [];
44960         this.grid.getDataSource().each(function(r) {
44961             val.push(r.data);
44962         });
44963         this.el.dom.value = Roo.encode(val);
44964     }
44965     
44966      
44967     
44968     
44969 });/*
44970  * Based on:
44971  * Ext JS Library 1.1.1
44972  * Copyright(c) 2006-2007, Ext JS, LLC.
44973  *
44974  * Originally Released Under LGPL - original licence link has changed is not relivant.
44975  *
44976  * Fork - LGPL
44977  * <script type="text/javascript">
44978  */
44979 /**
44980  * @class Roo.form.DisplayField
44981  * @extends Roo.form.Field
44982  * A generic Field to display non-editable data.
44983  * @constructor
44984  * Creates a new Display Field item.
44985  * @param {Object} config Configuration options
44986  */
44987 Roo.form.DisplayField = function(config){
44988     Roo.form.DisplayField.superclass.constructor.call(this, config);
44989     
44990 };
44991
44992 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
44993     inputType:      'hidden',
44994     allowBlank:     true,
44995     readOnly:         true,
44996     
44997  
44998     /**
44999      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45000      */
45001     focusClass : undefined,
45002     /**
45003      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45004      */
45005     fieldClass: 'x-form-field',
45006     
45007      /**
45008      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45009      */
45010     valueRenderer: undefined,
45011     
45012     width: 100,
45013     /**
45014      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45015      * {tag: "input", type: "checkbox", autocomplete: "off"})
45016      */
45017      
45018  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45019
45020     onResize : function(){
45021         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45022         
45023     },
45024
45025     initEvents : function(){
45026         // Roo.form.Checkbox.superclass.initEvents.call(this);
45027         // has no events...
45028        
45029     },
45030
45031
45032     getResizeEl : function(){
45033         return this.wrap;
45034     },
45035
45036     getPositionEl : function(){
45037         return this.wrap;
45038     },
45039
45040     // private
45041     onRender : function(ct, position){
45042         
45043         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
45044         //if(this.inputValue !== undefined){
45045         this.wrap = this.el.wrap();
45046         
45047         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
45048         
45049         if (this.bodyStyle) {
45050             this.viewEl.applyStyles(this.bodyStyle);
45051         }
45052         //this.viewEl.setStyle('padding', '2px');
45053         
45054         this.setValue(this.value);
45055         
45056     },
45057 /*
45058     // private
45059     initValue : Roo.emptyFn,
45060
45061   */
45062
45063         // private
45064     onClick : function(){
45065         
45066     },
45067
45068     /**
45069      * Sets the checked state of the checkbox.
45070      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
45071      */
45072     setValue : function(v){
45073         this.value = v;
45074         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
45075         // this might be called before we have a dom element..
45076         if (!this.viewEl) {
45077             return;
45078         }
45079         this.viewEl.dom.innerHTML = html;
45080         Roo.form.DisplayField.superclass.setValue.call(this, v);
45081
45082     }
45083 });/*
45084  * 
45085  * Licence- LGPL
45086  * 
45087  */
45088
45089 /**
45090  * @class Roo.form.DayPicker
45091  * @extends Roo.form.Field
45092  * A Day picker show [M] [T] [W] ....
45093  * @constructor
45094  * Creates a new Day Picker
45095  * @param {Object} config Configuration options
45096  */
45097 Roo.form.DayPicker= function(config){
45098     Roo.form.DayPicker.superclass.constructor.call(this, config);
45099      
45100 };
45101
45102 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
45103     /**
45104      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45105      */
45106     focusClass : undefined,
45107     /**
45108      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45109      */
45110     fieldClass: "x-form-field",
45111    
45112     /**
45113      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45114      * {tag: "input", type: "checkbox", autocomplete: "off"})
45115      */
45116     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
45117     
45118    
45119     actionMode : 'viewEl', 
45120     //
45121     // private
45122  
45123     inputType : 'hidden',
45124     
45125      
45126     inputElement: false, // real input element?
45127     basedOn: false, // ????
45128     
45129     isFormField: true, // not sure where this is needed!!!!
45130
45131     onResize : function(){
45132         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
45133         if(!this.boxLabel){
45134             this.el.alignTo(this.wrap, 'c-c');
45135         }
45136     },
45137
45138     initEvents : function(){
45139         Roo.form.Checkbox.superclass.initEvents.call(this);
45140         this.el.on("click", this.onClick,  this);
45141         this.el.on("change", this.onClick,  this);
45142     },
45143
45144
45145     getResizeEl : function(){
45146         return this.wrap;
45147     },
45148
45149     getPositionEl : function(){
45150         return this.wrap;
45151     },
45152
45153     
45154     // private
45155     onRender : function(ct, position){
45156         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45157        
45158         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
45159         
45160         var r1 = '<table><tr>';
45161         var r2 = '<tr class="x-form-daypick-icons">';
45162         for (var i=0; i < 7; i++) {
45163             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
45164             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
45165         }
45166         
45167         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
45168         viewEl.select('img').on('click', this.onClick, this);
45169         this.viewEl = viewEl;   
45170         
45171         
45172         // this will not work on Chrome!!!
45173         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
45174         this.el.on('propertychange', this.setFromHidden,  this);  //ie
45175         
45176         
45177           
45178
45179     },
45180
45181     // private
45182     initValue : Roo.emptyFn,
45183
45184     /**
45185      * Returns the checked state of the checkbox.
45186      * @return {Boolean} True if checked, else false
45187      */
45188     getValue : function(){
45189         return this.el.dom.value;
45190         
45191     },
45192
45193         // private
45194     onClick : function(e){ 
45195         //this.setChecked(!this.checked);
45196         Roo.get(e.target).toggleClass('x-menu-item-checked');
45197         this.refreshValue();
45198         //if(this.el.dom.checked != this.checked){
45199         //    this.setValue(this.el.dom.checked);
45200        // }
45201     },
45202     
45203     // private
45204     refreshValue : function()
45205     {
45206         var val = '';
45207         this.viewEl.select('img',true).each(function(e,i,n)  {
45208             val += e.is(".x-menu-item-checked") ? String(n) : '';
45209         });
45210         this.setValue(val, true);
45211     },
45212
45213     /**
45214      * Sets the checked state of the checkbox.
45215      * On is always based on a string comparison between inputValue and the param.
45216      * @param {Boolean/String} value - the value to set 
45217      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
45218      */
45219     setValue : function(v,suppressEvent){
45220         if (!this.el.dom) {
45221             return;
45222         }
45223         var old = this.el.dom.value ;
45224         this.el.dom.value = v;
45225         if (suppressEvent) {
45226             return ;
45227         }
45228          
45229         // update display..
45230         this.viewEl.select('img',true).each(function(e,i,n)  {
45231             
45232             var on = e.is(".x-menu-item-checked");
45233             var newv = v.indexOf(String(n)) > -1;
45234             if (on != newv) {
45235                 e.toggleClass('x-menu-item-checked');
45236             }
45237             
45238         });
45239         
45240         
45241         this.fireEvent('change', this, v, old);
45242         
45243         
45244     },
45245    
45246     // handle setting of hidden value by some other method!!?!?
45247     setFromHidden: function()
45248     {
45249         if(!this.el){
45250             return;
45251         }
45252         //console.log("SET FROM HIDDEN");
45253         //alert('setFrom hidden');
45254         this.setValue(this.el.dom.value);
45255     },
45256     
45257     onDestroy : function()
45258     {
45259         if(this.viewEl){
45260             Roo.get(this.viewEl).remove();
45261         }
45262          
45263         Roo.form.DayPicker.superclass.onDestroy.call(this);
45264     }
45265
45266 });/*
45267  * RooJS Library 1.1.1
45268  * Copyright(c) 2008-2011  Alan Knowles
45269  *
45270  * License - LGPL
45271  */
45272  
45273
45274 /**
45275  * @class Roo.form.ComboCheck
45276  * @extends Roo.form.ComboBox
45277  * A combobox for multiple select items.
45278  *
45279  * FIXME - could do with a reset button..
45280  * 
45281  * @constructor
45282  * Create a new ComboCheck
45283  * @param {Object} config Configuration options
45284  */
45285 Roo.form.ComboCheck = function(config){
45286     Roo.form.ComboCheck.superclass.constructor.call(this, config);
45287     // should verify some data...
45288     // like
45289     // hiddenName = required..
45290     // displayField = required
45291     // valudField == required
45292     var req= [ 'hiddenName', 'displayField', 'valueField' ];
45293     var _t = this;
45294     Roo.each(req, function(e) {
45295         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
45296             throw "Roo.form.ComboCheck : missing value for: " + e;
45297         }
45298     });
45299     
45300     
45301 };
45302
45303 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
45304      
45305      
45306     editable : false,
45307      
45308     selectedClass: 'x-menu-item-checked', 
45309     
45310     // private
45311     onRender : function(ct, position){
45312         var _t = this;
45313         
45314         
45315         
45316         if(!this.tpl){
45317             var cls = 'x-combo-list';
45318
45319             
45320             this.tpl =  new Roo.Template({
45321                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
45322                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
45323                    '<span>{' + this.displayField + '}</span>' +
45324                     '</div>' 
45325                 
45326             });
45327         }
45328  
45329         
45330         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
45331         this.view.singleSelect = false;
45332         this.view.multiSelect = true;
45333         this.view.toggleSelect = true;
45334         this.pageTb.add(new Roo.Toolbar.Fill(), {
45335             
45336             text: 'Done',
45337             handler: function()
45338             {
45339                 _t.collapse();
45340             }
45341         });
45342     },
45343     
45344     onViewOver : function(e, t){
45345         // do nothing...
45346         return;
45347         
45348     },
45349     
45350     onViewClick : function(doFocus,index){
45351         return;
45352         
45353     },
45354     select: function () {
45355         //Roo.log("SELECT CALLED");
45356     },
45357      
45358     selectByValue : function(xv, scrollIntoView){
45359         var ar = this.getValueArray();
45360         var sels = [];
45361         
45362         Roo.each(ar, function(v) {
45363             if(v === undefined || v === null){
45364                 return;
45365             }
45366             var r = this.findRecord(this.valueField, v);
45367             if(r){
45368                 sels.push(this.store.indexOf(r))
45369                 
45370             }
45371         },this);
45372         this.view.select(sels);
45373         return false;
45374     },
45375     
45376     
45377     
45378     onSelect : function(record, index){
45379        // Roo.log("onselect Called");
45380        // this is only called by the clear button now..
45381         this.view.clearSelections();
45382         this.setValue('[]');
45383         if (this.value != this.valueBefore) {
45384             this.fireEvent('change', this, this.value, this.valueBefore);
45385         }
45386     },
45387     getValueArray : function()
45388     {
45389         var ar = [] ;
45390         
45391         try {
45392             //Roo.log(this.value);
45393             if (typeof(this.value) == 'undefined') {
45394                 return [];
45395             }
45396             var ar = Roo.decode(this.value);
45397             return  ar instanceof Array ? ar : []; //?? valid?
45398             
45399         } catch(e) {
45400             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
45401             return [];
45402         }
45403          
45404     },
45405     expand : function ()
45406     {
45407         Roo.form.ComboCheck.superclass.expand.call(this);
45408         this.valueBefore = this.value;
45409         
45410
45411     },
45412     
45413     collapse : function(){
45414         Roo.form.ComboCheck.superclass.collapse.call(this);
45415         var sl = this.view.getSelectedIndexes();
45416         var st = this.store;
45417         var nv = [];
45418         var tv = [];
45419         var r;
45420         Roo.each(sl, function(i) {
45421             r = st.getAt(i);
45422             nv.push(r.get(this.valueField));
45423         },this);
45424         this.setValue(Roo.encode(nv));
45425         if (this.value != this.valueBefore) {
45426
45427             this.fireEvent('change', this, this.value, this.valueBefore);
45428         }
45429         
45430     },
45431     
45432     setValue : function(v){
45433         // Roo.log(v);
45434         this.value = v;
45435         
45436         var vals = this.getValueArray();
45437         var tv = [];
45438         Roo.each(vals, function(k) {
45439             var r = this.findRecord(this.valueField, k);
45440             if(r){
45441                 tv.push(r.data[this.displayField]);
45442             }else if(this.valueNotFoundText !== undefined){
45443                 tv.push( this.valueNotFoundText );
45444             }
45445         },this);
45446        // Roo.log(tv);
45447         
45448         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
45449         this.hiddenField.value = v;
45450         this.value = v;
45451     }
45452     
45453 });//<script type="text/javasscript">
45454  
45455
45456 /**
45457  * @class Roo.DDView
45458  * A DnD enabled version of Roo.View.
45459  * @param {Element/String} container The Element in which to create the View.
45460  * @param {String} tpl The template string used to create the markup for each element of the View
45461  * @param {Object} config The configuration properties. These include all the config options of
45462  * {@link Roo.View} plus some specific to this class.<br>
45463  * <p>
45464  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
45465  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
45466  * <p>
45467  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
45468 .x-view-drag-insert-above {
45469         border-top:1px dotted #3366cc;
45470 }
45471 .x-view-drag-insert-below {
45472         border-bottom:1px dotted #3366cc;
45473 }
45474 </code></pre>
45475  * 
45476  */
45477  
45478 Roo.DDView = function(container, tpl, config) {
45479     Roo.DDView.superclass.constructor.apply(this, arguments);
45480     this.getEl().setStyle("outline", "0px none");
45481     this.getEl().unselectable();
45482     if (this.dragGroup) {
45483                 this.setDraggable(this.dragGroup.split(","));
45484     }
45485     if (this.dropGroup) {
45486                 this.setDroppable(this.dropGroup.split(","));
45487     }
45488     if (this.deletable) {
45489         this.setDeletable();
45490     }
45491     this.isDirtyFlag = false;
45492         this.addEvents({
45493                 "drop" : true
45494         });
45495 };
45496
45497 Roo.extend(Roo.DDView, Roo.View, {
45498 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
45499 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
45500 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
45501 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
45502
45503         isFormField: true,
45504
45505         reset: Roo.emptyFn,
45506         
45507         clearInvalid: Roo.form.Field.prototype.clearInvalid,
45508
45509         validate: function() {
45510                 return true;
45511         },
45512         
45513         destroy: function() {
45514                 this.purgeListeners();
45515                 this.getEl.removeAllListeners();
45516                 this.getEl().remove();
45517                 if (this.dragZone) {
45518                         if (this.dragZone.destroy) {
45519                                 this.dragZone.destroy();
45520                         }
45521                 }
45522                 if (this.dropZone) {
45523                         if (this.dropZone.destroy) {
45524                                 this.dropZone.destroy();
45525                         }
45526                 }
45527         },
45528
45529 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
45530         getName: function() {
45531                 return this.name;
45532         },
45533
45534 /**     Loads the View from a JSON string representing the Records to put into the Store. */
45535         setValue: function(v) {
45536                 if (!this.store) {
45537                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
45538                 }
45539                 var data = {};
45540                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
45541                 this.store.proxy = new Roo.data.MemoryProxy(data);
45542                 this.store.load();
45543         },
45544
45545 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
45546         getValue: function() {
45547                 var result = '(';
45548                 this.store.each(function(rec) {
45549                         result += rec.id + ',';
45550                 });
45551                 return result.substr(0, result.length - 1) + ')';
45552         },
45553         
45554         getIds: function() {
45555                 var i = 0, result = new Array(this.store.getCount());
45556                 this.store.each(function(rec) {
45557                         result[i++] = rec.id;
45558                 });
45559                 return result;
45560         },
45561         
45562         isDirty: function() {
45563                 return this.isDirtyFlag;
45564         },
45565
45566 /**
45567  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
45568  *      whole Element becomes the target, and this causes the drop gesture to append.
45569  */
45570     getTargetFromEvent : function(e) {
45571                 var target = e.getTarget();
45572                 while ((target !== null) && (target.parentNode != this.el.dom)) {
45573                 target = target.parentNode;
45574                 }
45575                 if (!target) {
45576                         target = this.el.dom.lastChild || this.el.dom;
45577                 }
45578                 return target;
45579     },
45580
45581 /**
45582  *      Create the drag data which consists of an object which has the property "ddel" as
45583  *      the drag proxy element. 
45584  */
45585     getDragData : function(e) {
45586         var target = this.findItemFromChild(e.getTarget());
45587                 if(target) {
45588                         this.handleSelection(e);
45589                         var selNodes = this.getSelectedNodes();
45590             var dragData = {
45591                 source: this,
45592                 copy: this.copy || (this.allowCopy && e.ctrlKey),
45593                 nodes: selNodes,
45594                 records: []
45595                         };
45596                         var selectedIndices = this.getSelectedIndexes();
45597                         for (var i = 0; i < selectedIndices.length; i++) {
45598                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
45599                         }
45600                         if (selNodes.length == 1) {
45601                                 dragData.ddel = target.cloneNode(true); // the div element
45602                         } else {
45603                                 var div = document.createElement('div'); // create the multi element drag "ghost"
45604                                 div.className = 'multi-proxy';
45605                                 for (var i = 0, len = selNodes.length; i < len; i++) {
45606                                         div.appendChild(selNodes[i].cloneNode(true));
45607                                 }
45608                                 dragData.ddel = div;
45609                         }
45610             //console.log(dragData)
45611             //console.log(dragData.ddel.innerHTML)
45612                         return dragData;
45613                 }
45614         //console.log('nodragData')
45615                 return false;
45616     },
45617     
45618 /**     Specify to which ddGroup items in this DDView may be dragged. */
45619     setDraggable: function(ddGroup) {
45620         if (ddGroup instanceof Array) {
45621                 Roo.each(ddGroup, this.setDraggable, this);
45622                 return;
45623         }
45624         if (this.dragZone) {
45625                 this.dragZone.addToGroup(ddGroup);
45626         } else {
45627                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
45628                                 containerScroll: true,
45629                                 ddGroup: ddGroup 
45630
45631                         });
45632 //                      Draggability implies selection. DragZone's mousedown selects the element.
45633                         if (!this.multiSelect) { this.singleSelect = true; }
45634
45635 //                      Wire the DragZone's handlers up to methods in *this*
45636                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
45637                 }
45638     },
45639
45640 /**     Specify from which ddGroup this DDView accepts drops. */
45641     setDroppable: function(ddGroup) {
45642         if (ddGroup instanceof Array) {
45643                 Roo.each(ddGroup, this.setDroppable, this);
45644                 return;
45645         }
45646         if (this.dropZone) {
45647                 this.dropZone.addToGroup(ddGroup);
45648         } else {
45649                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
45650                                 containerScroll: true,
45651                                 ddGroup: ddGroup
45652                         });
45653
45654 //                      Wire the DropZone's handlers up to methods in *this*
45655                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
45656                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
45657                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
45658                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
45659                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
45660                 }
45661     },
45662
45663 /**     Decide whether to drop above or below a View node. */
45664     getDropPoint : function(e, n, dd){
45665         if (n == this.el.dom) { return "above"; }
45666                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
45667                 var c = t + (b - t) / 2;
45668                 var y = Roo.lib.Event.getPageY(e);
45669                 if(y <= c) {
45670                         return "above";
45671                 }else{
45672                         return "below";
45673                 }
45674     },
45675
45676     onNodeEnter : function(n, dd, e, data){
45677                 return false;
45678     },
45679     
45680     onNodeOver : function(n, dd, e, data){
45681                 var pt = this.getDropPoint(e, n, dd);
45682                 // set the insert point style on the target node
45683                 var dragElClass = this.dropNotAllowed;
45684                 if (pt) {
45685                         var targetElClass;
45686                         if (pt == "above"){
45687                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
45688                                 targetElClass = "x-view-drag-insert-above";
45689                         } else {
45690                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
45691                                 targetElClass = "x-view-drag-insert-below";
45692                         }
45693                         if (this.lastInsertClass != targetElClass){
45694                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
45695                                 this.lastInsertClass = targetElClass;
45696                         }
45697                 }
45698                 return dragElClass;
45699         },
45700
45701     onNodeOut : function(n, dd, e, data){
45702                 this.removeDropIndicators(n);
45703     },
45704
45705     onNodeDrop : function(n, dd, e, data){
45706         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
45707                 return false;
45708         }
45709         var pt = this.getDropPoint(e, n, dd);
45710                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
45711                 if (pt == "below") { insertAt++; }
45712                 for (var i = 0; i < data.records.length; i++) {
45713                         var r = data.records[i];
45714                         var dup = this.store.getById(r.id);
45715                         if (dup && (dd != this.dragZone)) {
45716                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
45717                         } else {
45718                                 if (data.copy) {
45719                                         this.store.insert(insertAt++, r.copy());
45720                                 } else {
45721                                         data.source.isDirtyFlag = true;
45722                                         r.store.remove(r);
45723                                         this.store.insert(insertAt++, r);
45724                                 }
45725                                 this.isDirtyFlag = true;
45726                         }
45727                 }
45728                 this.dragZone.cachedTarget = null;
45729                 return true;
45730     },
45731
45732     removeDropIndicators : function(n){
45733                 if(n){
45734                         Roo.fly(n).removeClass([
45735                                 "x-view-drag-insert-above",
45736                                 "x-view-drag-insert-below"]);
45737                         this.lastInsertClass = "_noclass";
45738                 }
45739     },
45740
45741 /**
45742  *      Utility method. Add a delete option to the DDView's context menu.
45743  *      @param {String} imageUrl The URL of the "delete" icon image.
45744  */
45745         setDeletable: function(imageUrl) {
45746                 if (!this.singleSelect && !this.multiSelect) {
45747                         this.singleSelect = true;
45748                 }
45749                 var c = this.getContextMenu();
45750                 this.contextMenu.on("itemclick", function(item) {
45751                         switch (item.id) {
45752                                 case "delete":
45753                                         this.remove(this.getSelectedIndexes());
45754                                         break;
45755                         }
45756                 }, this);
45757                 this.contextMenu.add({
45758                         icon: imageUrl,
45759                         id: "delete",
45760                         text: 'Delete'
45761                 });
45762         },
45763         
45764 /**     Return the context menu for this DDView. */
45765         getContextMenu: function() {
45766                 if (!this.contextMenu) {
45767 //                      Create the View's context menu
45768                         this.contextMenu = new Roo.menu.Menu({
45769                                 id: this.id + "-contextmenu"
45770                         });
45771                         this.el.on("contextmenu", this.showContextMenu, this);
45772                 }
45773                 return this.contextMenu;
45774         },
45775         
45776         disableContextMenu: function() {
45777                 if (this.contextMenu) {
45778                         this.el.un("contextmenu", this.showContextMenu, this);
45779                 }
45780         },
45781
45782         showContextMenu: function(e, item) {
45783         item = this.findItemFromChild(e.getTarget());
45784                 if (item) {
45785                         e.stopEvent();
45786                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
45787                         this.contextMenu.showAt(e.getXY());
45788             }
45789     },
45790
45791 /**
45792  *      Remove {@link Roo.data.Record}s at the specified indices.
45793  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
45794  */
45795     remove: function(selectedIndices) {
45796                 selectedIndices = [].concat(selectedIndices);
45797                 for (var i = 0; i < selectedIndices.length; i++) {
45798                         var rec = this.store.getAt(selectedIndices[i]);
45799                         this.store.remove(rec);
45800                 }
45801     },
45802
45803 /**
45804  *      Double click fires the event, but also, if this is draggable, and there is only one other
45805  *      related DropZone, it transfers the selected node.
45806  */
45807     onDblClick : function(e){
45808         var item = this.findItemFromChild(e.getTarget());
45809         if(item){
45810             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
45811                 return false;
45812             }
45813             if (this.dragGroup) {
45814                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
45815                     while (targets.indexOf(this.dropZone) > -1) {
45816                             targets.remove(this.dropZone);
45817                                 }
45818                     if (targets.length == 1) {
45819                                         this.dragZone.cachedTarget = null;
45820                         var el = Roo.get(targets[0].getEl());
45821                         var box = el.getBox(true);
45822                         targets[0].onNodeDrop(el.dom, {
45823                                 target: el.dom,
45824                                 xy: [box.x, box.y + box.height - 1]
45825                         }, null, this.getDragData(e));
45826                     }
45827                 }
45828         }
45829     },
45830     
45831     handleSelection: function(e) {
45832                 this.dragZone.cachedTarget = null;
45833         var item = this.findItemFromChild(e.getTarget());
45834         if (!item) {
45835                 this.clearSelections(true);
45836                 return;
45837         }
45838                 if (item && (this.multiSelect || this.singleSelect)){
45839                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
45840                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
45841                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
45842                                 this.unselect(item);
45843                         } else {
45844                                 this.select(item, this.multiSelect && e.ctrlKey);
45845                                 this.lastSelection = item;
45846                         }
45847                 }
45848     },
45849
45850     onItemClick : function(item, index, e){
45851                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
45852                         return false;
45853                 }
45854                 return true;
45855     },
45856
45857     unselect : function(nodeInfo, suppressEvent){
45858                 var node = this.getNode(nodeInfo);
45859                 if(node && this.isSelected(node)){
45860                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
45861                                 Roo.fly(node).removeClass(this.selectedClass);
45862                                 this.selections.remove(node);
45863                                 if(!suppressEvent){
45864                                         this.fireEvent("selectionchange", this, this.selections);
45865                                 }
45866                         }
45867                 }
45868     }
45869 });
45870 /*
45871  * Based on:
45872  * Ext JS Library 1.1.1
45873  * Copyright(c) 2006-2007, Ext JS, LLC.
45874  *
45875  * Originally Released Under LGPL - original licence link has changed is not relivant.
45876  *
45877  * Fork - LGPL
45878  * <script type="text/javascript">
45879  */
45880  
45881 /**
45882  * @class Roo.LayoutManager
45883  * @extends Roo.util.Observable
45884  * Base class for layout managers.
45885  */
45886 Roo.LayoutManager = function(container, config){
45887     Roo.LayoutManager.superclass.constructor.call(this);
45888     this.el = Roo.get(container);
45889     // ie scrollbar fix
45890     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
45891         document.body.scroll = "no";
45892     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
45893         this.el.position('relative');
45894     }
45895     this.id = this.el.id;
45896     this.el.addClass("x-layout-container");
45897     /** false to disable window resize monitoring @type Boolean */
45898     this.monitorWindowResize = true;
45899     this.regions = {};
45900     this.addEvents({
45901         /**
45902          * @event layout
45903          * Fires when a layout is performed. 
45904          * @param {Roo.LayoutManager} this
45905          */
45906         "layout" : true,
45907         /**
45908          * @event regionresized
45909          * Fires when the user resizes a region. 
45910          * @param {Roo.LayoutRegion} region The resized region
45911          * @param {Number} newSize The new size (width for east/west, height for north/south)
45912          */
45913         "regionresized" : true,
45914         /**
45915          * @event regioncollapsed
45916          * Fires when a region is collapsed. 
45917          * @param {Roo.LayoutRegion} region The collapsed region
45918          */
45919         "regioncollapsed" : true,
45920         /**
45921          * @event regionexpanded
45922          * Fires when a region is expanded.  
45923          * @param {Roo.LayoutRegion} region The expanded region
45924          */
45925         "regionexpanded" : true
45926     });
45927     this.updating = false;
45928     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
45929 };
45930
45931 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
45932     /**
45933      * Returns true if this layout is currently being updated
45934      * @return {Boolean}
45935      */
45936     isUpdating : function(){
45937         return this.updating; 
45938     },
45939     
45940     /**
45941      * Suspend the LayoutManager from doing auto-layouts while
45942      * making multiple add or remove calls
45943      */
45944     beginUpdate : function(){
45945         this.updating = true;    
45946     },
45947     
45948     /**
45949      * Restore auto-layouts and optionally disable the manager from performing a layout
45950      * @param {Boolean} noLayout true to disable a layout update 
45951      */
45952     endUpdate : function(noLayout){
45953         this.updating = false;
45954         if(!noLayout){
45955             this.layout();
45956         }    
45957     },
45958     
45959     layout: function(){
45960         
45961     },
45962     
45963     onRegionResized : function(region, newSize){
45964         this.fireEvent("regionresized", region, newSize);
45965         this.layout();
45966     },
45967     
45968     onRegionCollapsed : function(region){
45969         this.fireEvent("regioncollapsed", region);
45970     },
45971     
45972     onRegionExpanded : function(region){
45973         this.fireEvent("regionexpanded", region);
45974     },
45975         
45976     /**
45977      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
45978      * performs box-model adjustments.
45979      * @return {Object} The size as an object {width: (the width), height: (the height)}
45980      */
45981     getViewSize : function(){
45982         var size;
45983         if(this.el.dom != document.body){
45984             size = this.el.getSize();
45985         }else{
45986             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
45987         }
45988         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
45989         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45990         return size;
45991     },
45992     
45993     /**
45994      * Returns the Element this layout is bound to.
45995      * @return {Roo.Element}
45996      */
45997     getEl : function(){
45998         return this.el;
45999     },
46000     
46001     /**
46002      * Returns the specified region.
46003      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
46004      * @return {Roo.LayoutRegion}
46005      */
46006     getRegion : function(target){
46007         return this.regions[target.toLowerCase()];
46008     },
46009     
46010     onWindowResize : function(){
46011         if(this.monitorWindowResize){
46012             this.layout();
46013         }
46014     }
46015 });/*
46016  * Based on:
46017  * Ext JS Library 1.1.1
46018  * Copyright(c) 2006-2007, Ext JS, LLC.
46019  *
46020  * Originally Released Under LGPL - original licence link has changed is not relivant.
46021  *
46022  * Fork - LGPL
46023  * <script type="text/javascript">
46024  */
46025 /**
46026  * @class Roo.BorderLayout
46027  * @extends Roo.LayoutManager
46028  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
46029  * please see: <br><br>
46030  * <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>
46031  * <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>
46032  * Example:
46033  <pre><code>
46034  var layout = new Roo.BorderLayout(document.body, {
46035     north: {
46036         initialSize: 25,
46037         titlebar: false
46038     },
46039     west: {
46040         split:true,
46041         initialSize: 200,
46042         minSize: 175,
46043         maxSize: 400,
46044         titlebar: true,
46045         collapsible: true
46046     },
46047     east: {
46048         split:true,
46049         initialSize: 202,
46050         minSize: 175,
46051         maxSize: 400,
46052         titlebar: true,
46053         collapsible: true
46054     },
46055     south: {
46056         split:true,
46057         initialSize: 100,
46058         minSize: 100,
46059         maxSize: 200,
46060         titlebar: true,
46061         collapsible: true
46062     },
46063     center: {
46064         titlebar: true,
46065         autoScroll:true,
46066         resizeTabs: true,
46067         minTabWidth: 50,
46068         preferredTabWidth: 150
46069     }
46070 });
46071
46072 // shorthand
46073 var CP = Roo.ContentPanel;
46074
46075 layout.beginUpdate();
46076 layout.add("north", new CP("north", "North"));
46077 layout.add("south", new CP("south", {title: "South", closable: true}));
46078 layout.add("west", new CP("west", {title: "West"}));
46079 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
46080 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
46081 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
46082 layout.getRegion("center").showPanel("center1");
46083 layout.endUpdate();
46084 </code></pre>
46085
46086 <b>The container the layout is rendered into can be either the body element or any other element.
46087 If it is not the body element, the container needs to either be an absolute positioned element,
46088 or you will need to add "position:relative" to the css of the container.  You will also need to specify
46089 the container size if it is not the body element.</b>
46090
46091 * @constructor
46092 * Create a new BorderLayout
46093 * @param {String/HTMLElement/Element} container The container this layout is bound to
46094 * @param {Object} config Configuration options
46095  */
46096 Roo.BorderLayout = function(container, config){
46097     config = config || {};
46098     Roo.BorderLayout.superclass.constructor.call(this, container, config);
46099     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
46100     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
46101         var target = this.factory.validRegions[i];
46102         if(config[target]){
46103             this.addRegion(target, config[target]);
46104         }
46105     }
46106 };
46107
46108 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
46109     /**
46110      * Creates and adds a new region if it doesn't already exist.
46111      * @param {String} target The target region key (north, south, east, west or center).
46112      * @param {Object} config The regions config object
46113      * @return {BorderLayoutRegion} The new region
46114      */
46115     addRegion : function(target, config){
46116         if(!this.regions[target]){
46117             var r = this.factory.create(target, this, config);
46118             this.bindRegion(target, r);
46119         }
46120         return this.regions[target];
46121     },
46122
46123     // private (kinda)
46124     bindRegion : function(name, r){
46125         this.regions[name] = r;
46126         r.on("visibilitychange", this.layout, this);
46127         r.on("paneladded", this.layout, this);
46128         r.on("panelremoved", this.layout, this);
46129         r.on("invalidated", this.layout, this);
46130         r.on("resized", this.onRegionResized, this);
46131         r.on("collapsed", this.onRegionCollapsed, this);
46132         r.on("expanded", this.onRegionExpanded, this);
46133     },
46134
46135     /**
46136      * Performs a layout update.
46137      */
46138     layout : function(){
46139         if(this.updating) return;
46140         var size = this.getViewSize();
46141         var w = size.width;
46142         var h = size.height;
46143         var centerW = w;
46144         var centerH = h;
46145         var centerY = 0;
46146         var centerX = 0;
46147         //var x = 0, y = 0;
46148
46149         var rs = this.regions;
46150         var north = rs["north"];
46151         var south = rs["south"]; 
46152         var west = rs["west"];
46153         var east = rs["east"];
46154         var center = rs["center"];
46155         //if(this.hideOnLayout){ // not supported anymore
46156             //c.el.setStyle("display", "none");
46157         //}
46158         if(north && north.isVisible()){
46159             var b = north.getBox();
46160             var m = north.getMargins();
46161             b.width = w - (m.left+m.right);
46162             b.x = m.left;
46163             b.y = m.top;
46164             centerY = b.height + b.y + m.bottom;
46165             centerH -= centerY;
46166             north.updateBox(this.safeBox(b));
46167         }
46168         if(south && south.isVisible()){
46169             var b = south.getBox();
46170             var m = south.getMargins();
46171             b.width = w - (m.left+m.right);
46172             b.x = m.left;
46173             var totalHeight = (b.height + m.top + m.bottom);
46174             b.y = h - totalHeight + m.top;
46175             centerH -= totalHeight;
46176             south.updateBox(this.safeBox(b));
46177         }
46178         if(west && west.isVisible()){
46179             var b = west.getBox();
46180             var m = west.getMargins();
46181             b.height = centerH - (m.top+m.bottom);
46182             b.x = m.left;
46183             b.y = centerY + m.top;
46184             var totalWidth = (b.width + m.left + m.right);
46185             centerX += totalWidth;
46186             centerW -= totalWidth;
46187             west.updateBox(this.safeBox(b));
46188         }
46189         if(east && east.isVisible()){
46190             var b = east.getBox();
46191             var m = east.getMargins();
46192             b.height = centerH - (m.top+m.bottom);
46193             var totalWidth = (b.width + m.left + m.right);
46194             b.x = w - totalWidth + m.left;
46195             b.y = centerY + m.top;
46196             centerW -= totalWidth;
46197             east.updateBox(this.safeBox(b));
46198         }
46199         if(center){
46200             var m = center.getMargins();
46201             var centerBox = {
46202                 x: centerX + m.left,
46203                 y: centerY + m.top,
46204                 width: centerW - (m.left+m.right),
46205                 height: centerH - (m.top+m.bottom)
46206             };
46207             //if(this.hideOnLayout){
46208                 //center.el.setStyle("display", "block");
46209             //}
46210             center.updateBox(this.safeBox(centerBox));
46211         }
46212         this.el.repaint();
46213         this.fireEvent("layout", this);
46214     },
46215
46216     // private
46217     safeBox : function(box){
46218         box.width = Math.max(0, box.width);
46219         box.height = Math.max(0, box.height);
46220         return box;
46221     },
46222
46223     /**
46224      * Adds a ContentPanel (or subclass) to this layout.
46225      * @param {String} target The target region key (north, south, east, west or center).
46226      * @param {Roo.ContentPanel} panel The panel to add
46227      * @return {Roo.ContentPanel} The added panel
46228      */
46229     add : function(target, panel){
46230          
46231         target = target.toLowerCase();
46232         return this.regions[target].add(panel);
46233     },
46234
46235     /**
46236      * Remove a ContentPanel (or subclass) to this layout.
46237      * @param {String} target The target region key (north, south, east, west or center).
46238      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
46239      * @return {Roo.ContentPanel} The removed panel
46240      */
46241     remove : function(target, panel){
46242         target = target.toLowerCase();
46243         return this.regions[target].remove(panel);
46244     },
46245
46246     /**
46247      * Searches all regions for a panel with the specified id
46248      * @param {String} panelId
46249      * @return {Roo.ContentPanel} The panel or null if it wasn't found
46250      */
46251     findPanel : function(panelId){
46252         var rs = this.regions;
46253         for(var target in rs){
46254             if(typeof rs[target] != "function"){
46255                 var p = rs[target].getPanel(panelId);
46256                 if(p){
46257                     return p;
46258                 }
46259             }
46260         }
46261         return null;
46262     },
46263
46264     /**
46265      * Searches all regions for a panel with the specified id and activates (shows) it.
46266      * @param {String/ContentPanel} panelId The panels id or the panel itself
46267      * @return {Roo.ContentPanel} The shown panel or null
46268      */
46269     showPanel : function(panelId) {
46270       var rs = this.regions;
46271       for(var target in rs){
46272          var r = rs[target];
46273          if(typeof r != "function"){
46274             if(r.hasPanel(panelId)){
46275                return r.showPanel(panelId);
46276             }
46277          }
46278       }
46279       return null;
46280    },
46281
46282    /**
46283      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
46284      * @param {Roo.state.Provider} provider (optional) An alternate state provider
46285      */
46286     restoreState : function(provider){
46287         if(!provider){
46288             provider = Roo.state.Manager;
46289         }
46290         var sm = new Roo.LayoutStateManager();
46291         sm.init(this, provider);
46292     },
46293
46294     /**
46295      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
46296      * object should contain properties for each region to add ContentPanels to, and each property's value should be
46297      * a valid ContentPanel config object.  Example:
46298      * <pre><code>
46299 // Create the main layout
46300 var layout = new Roo.BorderLayout('main-ct', {
46301     west: {
46302         split:true,
46303         minSize: 175,
46304         titlebar: true
46305     },
46306     center: {
46307         title:'Components'
46308     }
46309 }, 'main-ct');
46310
46311 // Create and add multiple ContentPanels at once via configs
46312 layout.batchAdd({
46313    west: {
46314        id: 'source-files',
46315        autoCreate:true,
46316        title:'Ext Source Files',
46317        autoScroll:true,
46318        fitToFrame:true
46319    },
46320    center : {
46321        el: cview,
46322        autoScroll:true,
46323        fitToFrame:true,
46324        toolbar: tb,
46325        resizeEl:'cbody'
46326    }
46327 });
46328 </code></pre>
46329      * @param {Object} regions An object containing ContentPanel configs by region name
46330      */
46331     batchAdd : function(regions){
46332         this.beginUpdate();
46333         for(var rname in regions){
46334             var lr = this.regions[rname];
46335             if(lr){
46336                 this.addTypedPanels(lr, regions[rname]);
46337             }
46338         }
46339         this.endUpdate();
46340     },
46341
46342     // private
46343     addTypedPanels : function(lr, ps){
46344         if(typeof ps == 'string'){
46345             lr.add(new Roo.ContentPanel(ps));
46346         }
46347         else if(ps instanceof Array){
46348             for(var i =0, len = ps.length; i < len; i++){
46349                 this.addTypedPanels(lr, ps[i]);
46350             }
46351         }
46352         else if(!ps.events){ // raw config?
46353             var el = ps.el;
46354             delete ps.el; // prevent conflict
46355             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
46356         }
46357         else {  // panel object assumed!
46358             lr.add(ps);
46359         }
46360     },
46361     /**
46362      * Adds a xtype elements to the layout.
46363      * <pre><code>
46364
46365 layout.addxtype({
46366        xtype : 'ContentPanel',
46367        region: 'west',
46368        items: [ .... ]
46369    }
46370 );
46371
46372 layout.addxtype({
46373         xtype : 'NestedLayoutPanel',
46374         region: 'west',
46375         layout: {
46376            center: { },
46377            west: { }   
46378         },
46379         items : [ ... list of content panels or nested layout panels.. ]
46380    }
46381 );
46382 </code></pre>
46383      * @param {Object} cfg Xtype definition of item to add.
46384      */
46385     addxtype : function(cfg)
46386     {
46387         // basically accepts a pannel...
46388         // can accept a layout region..!?!?
46389         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
46390         
46391         if (!cfg.xtype.match(/Panel$/)) {
46392             return false;
46393         }
46394         var ret = false;
46395         
46396         if (typeof(cfg.region) == 'undefined') {
46397             Roo.log("Failed to add Panel, region was not set");
46398             Roo.log(cfg);
46399             return false;
46400         }
46401         var region = cfg.region;
46402         delete cfg.region;
46403         
46404           
46405         var xitems = [];
46406         if (cfg.items) {
46407             xitems = cfg.items;
46408             delete cfg.items;
46409         }
46410         var nb = false;
46411         
46412         switch(cfg.xtype) 
46413         {
46414             case 'ContentPanel':  // ContentPanel (el, cfg)
46415             case 'ScrollPanel':  // ContentPanel (el, cfg)
46416                 if(cfg.autoCreate) {
46417                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
46418                 } else {
46419                     var el = this.el.createChild();
46420                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
46421                 }
46422                 
46423                 this.add(region, ret);
46424                 break;
46425             
46426             
46427             case 'TreePanel': // our new panel!
46428                 cfg.el = this.el.createChild();
46429                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
46430                 this.add(region, ret);
46431                 break;
46432             
46433             case 'NestedLayoutPanel': 
46434                 // create a new Layout (which is  a Border Layout...
46435                 var el = this.el.createChild();
46436                 var clayout = cfg.layout;
46437                 delete cfg.layout;
46438                 clayout.items   = clayout.items  || [];
46439                 // replace this exitems with the clayout ones..
46440                 xitems = clayout.items;
46441                  
46442                 
46443                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
46444                     cfg.background = false;
46445                 }
46446                 var layout = new Roo.BorderLayout(el, clayout);
46447                 
46448                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
46449                 //console.log('adding nested layout panel '  + cfg.toSource());
46450                 this.add(region, ret);
46451                 nb = {}; /// find first...
46452                 break;
46453                 
46454             case 'GridPanel': 
46455             
46456                 // needs grid and region
46457                 
46458                 //var el = this.getRegion(region).el.createChild();
46459                 var el = this.el.createChild();
46460                 // create the grid first...
46461                 
46462                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
46463                 delete cfg.grid;
46464                 if (region == 'center' && this.active ) {
46465                     cfg.background = false;
46466                 }
46467                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
46468                 
46469                 this.add(region, ret);
46470                 if (cfg.background) {
46471                     ret.on('activate', function(gp) {
46472                         if (!gp.grid.rendered) {
46473                             gp.grid.render();
46474                         }
46475                     });
46476                 } else {
46477                     grid.render();
46478                 }
46479                 break;
46480            
46481                
46482                 
46483                 
46484             default: 
46485                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
46486                 return null;
46487              // GridPanel (grid, cfg)
46488             
46489         }
46490         this.beginUpdate();
46491         // add children..
46492         var region = '';
46493         var abn = {};
46494         Roo.each(xitems, function(i)  {
46495             region = nb && i.region ? i.region : false;
46496             
46497             var add = ret.addxtype(i);
46498            
46499             if (region) {
46500                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
46501                 if (!i.background) {
46502                     abn[region] = nb[region] ;
46503                 }
46504             }
46505             
46506         });
46507         this.endUpdate();
46508
46509         // make the last non-background panel active..
46510         //if (nb) { Roo.log(abn); }
46511         if (nb) {
46512             
46513             for(var r in abn) {
46514                 region = this.getRegion(r);
46515                 if (region) {
46516                     // tried using nb[r], but it does not work..
46517                      
46518                     region.showPanel(abn[r]);
46519                    
46520                 }
46521             }
46522         }
46523         return ret;
46524         
46525     }
46526 });
46527
46528 /**
46529  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
46530  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
46531  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
46532  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
46533  * <pre><code>
46534 // shorthand
46535 var CP = Roo.ContentPanel;
46536
46537 var layout = Roo.BorderLayout.create({
46538     north: {
46539         initialSize: 25,
46540         titlebar: false,
46541         panels: [new CP("north", "North")]
46542     },
46543     west: {
46544         split:true,
46545         initialSize: 200,
46546         minSize: 175,
46547         maxSize: 400,
46548         titlebar: true,
46549         collapsible: true,
46550         panels: [new CP("west", {title: "West"})]
46551     },
46552     east: {
46553         split:true,
46554         initialSize: 202,
46555         minSize: 175,
46556         maxSize: 400,
46557         titlebar: true,
46558         collapsible: true,
46559         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
46560     },
46561     south: {
46562         split:true,
46563         initialSize: 100,
46564         minSize: 100,
46565         maxSize: 200,
46566         titlebar: true,
46567         collapsible: true,
46568         panels: [new CP("south", {title: "South", closable: true})]
46569     },
46570     center: {
46571         titlebar: true,
46572         autoScroll:true,
46573         resizeTabs: true,
46574         minTabWidth: 50,
46575         preferredTabWidth: 150,
46576         panels: [
46577             new CP("center1", {title: "Close Me", closable: true}),
46578             new CP("center2", {title: "Center Panel", closable: false})
46579         ]
46580     }
46581 }, document.body);
46582
46583 layout.getRegion("center").showPanel("center1");
46584 </code></pre>
46585  * @param config
46586  * @param targetEl
46587  */
46588 Roo.BorderLayout.create = function(config, targetEl){
46589     var layout = new Roo.BorderLayout(targetEl || document.body, config);
46590     layout.beginUpdate();
46591     var regions = Roo.BorderLayout.RegionFactory.validRegions;
46592     for(var j = 0, jlen = regions.length; j < jlen; j++){
46593         var lr = regions[j];
46594         if(layout.regions[lr] && config[lr].panels){
46595             var r = layout.regions[lr];
46596             var ps = config[lr].panels;
46597             layout.addTypedPanels(r, ps);
46598         }
46599     }
46600     layout.endUpdate();
46601     return layout;
46602 };
46603
46604 // private
46605 Roo.BorderLayout.RegionFactory = {
46606     // private
46607     validRegions : ["north","south","east","west","center"],
46608
46609     // private
46610     create : function(target, mgr, config){
46611         target = target.toLowerCase();
46612         if(config.lightweight || config.basic){
46613             return new Roo.BasicLayoutRegion(mgr, config, target);
46614         }
46615         switch(target){
46616             case "north":
46617                 return new Roo.NorthLayoutRegion(mgr, config);
46618             case "south":
46619                 return new Roo.SouthLayoutRegion(mgr, config);
46620             case "east":
46621                 return new Roo.EastLayoutRegion(mgr, config);
46622             case "west":
46623                 return new Roo.WestLayoutRegion(mgr, config);
46624             case "center":
46625                 return new Roo.CenterLayoutRegion(mgr, config);
46626         }
46627         throw 'Layout region "'+target+'" not supported.';
46628     }
46629 };/*
46630  * Based on:
46631  * Ext JS Library 1.1.1
46632  * Copyright(c) 2006-2007, Ext JS, LLC.
46633  *
46634  * Originally Released Under LGPL - original licence link has changed is not relivant.
46635  *
46636  * Fork - LGPL
46637  * <script type="text/javascript">
46638  */
46639  
46640 /**
46641  * @class Roo.BasicLayoutRegion
46642  * @extends Roo.util.Observable
46643  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
46644  * and does not have a titlebar, tabs or any other features. All it does is size and position 
46645  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
46646  */
46647 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
46648     this.mgr = mgr;
46649     this.position  = pos;
46650     this.events = {
46651         /**
46652          * @scope Roo.BasicLayoutRegion
46653          */
46654         
46655         /**
46656          * @event beforeremove
46657          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
46658          * @param {Roo.LayoutRegion} this
46659          * @param {Roo.ContentPanel} panel The panel
46660          * @param {Object} e The cancel event object
46661          */
46662         "beforeremove" : true,
46663         /**
46664          * @event invalidated
46665          * Fires when the layout for this region is changed.
46666          * @param {Roo.LayoutRegion} this
46667          */
46668         "invalidated" : true,
46669         /**
46670          * @event visibilitychange
46671          * Fires when this region is shown or hidden 
46672          * @param {Roo.LayoutRegion} this
46673          * @param {Boolean} visibility true or false
46674          */
46675         "visibilitychange" : true,
46676         /**
46677          * @event paneladded
46678          * Fires when a panel is added. 
46679          * @param {Roo.LayoutRegion} this
46680          * @param {Roo.ContentPanel} panel The panel
46681          */
46682         "paneladded" : true,
46683         /**
46684          * @event panelremoved
46685          * Fires when a panel is removed. 
46686          * @param {Roo.LayoutRegion} this
46687          * @param {Roo.ContentPanel} panel The panel
46688          */
46689         "panelremoved" : true,
46690         /**
46691          * @event collapsed
46692          * Fires when this region is collapsed.
46693          * @param {Roo.LayoutRegion} this
46694          */
46695         "collapsed" : true,
46696         /**
46697          * @event expanded
46698          * Fires when this region is expanded.
46699          * @param {Roo.LayoutRegion} this
46700          */
46701         "expanded" : true,
46702         /**
46703          * @event slideshow
46704          * Fires when this region is slid into view.
46705          * @param {Roo.LayoutRegion} this
46706          */
46707         "slideshow" : true,
46708         /**
46709          * @event slidehide
46710          * Fires when this region slides out of view. 
46711          * @param {Roo.LayoutRegion} this
46712          */
46713         "slidehide" : true,
46714         /**
46715          * @event panelactivated
46716          * Fires when a panel is activated. 
46717          * @param {Roo.LayoutRegion} this
46718          * @param {Roo.ContentPanel} panel The activated panel
46719          */
46720         "panelactivated" : true,
46721         /**
46722          * @event resized
46723          * Fires when the user resizes this region. 
46724          * @param {Roo.LayoutRegion} this
46725          * @param {Number} newSize The new size (width for east/west, height for north/south)
46726          */
46727         "resized" : true
46728     };
46729     /** A collection of panels in this region. @type Roo.util.MixedCollection */
46730     this.panels = new Roo.util.MixedCollection();
46731     this.panels.getKey = this.getPanelId.createDelegate(this);
46732     this.box = null;
46733     this.activePanel = null;
46734     // ensure listeners are added...
46735     
46736     if (config.listeners || config.events) {
46737         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
46738             listeners : config.listeners || {},
46739             events : config.events || {}
46740         });
46741     }
46742     
46743     if(skipConfig !== true){
46744         this.applyConfig(config);
46745     }
46746 };
46747
46748 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
46749     getPanelId : function(p){
46750         return p.getId();
46751     },
46752     
46753     applyConfig : function(config){
46754         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
46755         this.config = config;
46756         
46757     },
46758     
46759     /**
46760      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
46761      * the width, for horizontal (north, south) the height.
46762      * @param {Number} newSize The new width or height
46763      */
46764     resizeTo : function(newSize){
46765         var el = this.el ? this.el :
46766                  (this.activePanel ? this.activePanel.getEl() : null);
46767         if(el){
46768             switch(this.position){
46769                 case "east":
46770                 case "west":
46771                     el.setWidth(newSize);
46772                     this.fireEvent("resized", this, newSize);
46773                 break;
46774                 case "north":
46775                 case "south":
46776                     el.setHeight(newSize);
46777                     this.fireEvent("resized", this, newSize);
46778                 break;                
46779             }
46780         }
46781     },
46782     
46783     getBox : function(){
46784         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
46785     },
46786     
46787     getMargins : function(){
46788         return this.margins;
46789     },
46790     
46791     updateBox : function(box){
46792         this.box = box;
46793         var el = this.activePanel.getEl();
46794         el.dom.style.left = box.x + "px";
46795         el.dom.style.top = box.y + "px";
46796         this.activePanel.setSize(box.width, box.height);
46797     },
46798     
46799     /**
46800      * Returns the container element for this region.
46801      * @return {Roo.Element}
46802      */
46803     getEl : function(){
46804         return this.activePanel;
46805     },
46806     
46807     /**
46808      * Returns true if this region is currently visible.
46809      * @return {Boolean}
46810      */
46811     isVisible : function(){
46812         return this.activePanel ? true : false;
46813     },
46814     
46815     setActivePanel : function(panel){
46816         panel = this.getPanel(panel);
46817         if(this.activePanel && this.activePanel != panel){
46818             this.activePanel.setActiveState(false);
46819             this.activePanel.getEl().setLeftTop(-10000,-10000);
46820         }
46821         this.activePanel = panel;
46822         panel.setActiveState(true);
46823         if(this.box){
46824             panel.setSize(this.box.width, this.box.height);
46825         }
46826         this.fireEvent("panelactivated", this, panel);
46827         this.fireEvent("invalidated");
46828     },
46829     
46830     /**
46831      * Show the specified panel.
46832      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
46833      * @return {Roo.ContentPanel} The shown panel or null
46834      */
46835     showPanel : function(panel){
46836         if(panel = this.getPanel(panel)){
46837             this.setActivePanel(panel);
46838         }
46839         return panel;
46840     },
46841     
46842     /**
46843      * Get the active panel for this region.
46844      * @return {Roo.ContentPanel} The active panel or null
46845      */
46846     getActivePanel : function(){
46847         return this.activePanel;
46848     },
46849     
46850     /**
46851      * Add the passed ContentPanel(s)
46852      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
46853      * @return {Roo.ContentPanel} The panel added (if only one was added)
46854      */
46855     add : function(panel){
46856         if(arguments.length > 1){
46857             for(var i = 0, len = arguments.length; i < len; i++) {
46858                 this.add(arguments[i]);
46859             }
46860             return null;
46861         }
46862         if(this.hasPanel(panel)){
46863             this.showPanel(panel);
46864             return panel;
46865         }
46866         var el = panel.getEl();
46867         if(el.dom.parentNode != this.mgr.el.dom){
46868             this.mgr.el.dom.appendChild(el.dom);
46869         }
46870         if(panel.setRegion){
46871             panel.setRegion(this);
46872         }
46873         this.panels.add(panel);
46874         el.setStyle("position", "absolute");
46875         if(!panel.background){
46876             this.setActivePanel(panel);
46877             if(this.config.initialSize && this.panels.getCount()==1){
46878                 this.resizeTo(this.config.initialSize);
46879             }
46880         }
46881         this.fireEvent("paneladded", this, panel);
46882         return panel;
46883     },
46884     
46885     /**
46886      * Returns true if the panel is in this region.
46887      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
46888      * @return {Boolean}
46889      */
46890     hasPanel : function(panel){
46891         if(typeof panel == "object"){ // must be panel obj
46892             panel = panel.getId();
46893         }
46894         return this.getPanel(panel) ? true : false;
46895     },
46896     
46897     /**
46898      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
46899      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
46900      * @param {Boolean} preservePanel Overrides the config preservePanel option
46901      * @return {Roo.ContentPanel} The panel that was removed
46902      */
46903     remove : function(panel, preservePanel){
46904         panel = this.getPanel(panel);
46905         if(!panel){
46906             return null;
46907         }
46908         var e = {};
46909         this.fireEvent("beforeremove", this, panel, e);
46910         if(e.cancel === true){
46911             return null;
46912         }
46913         var panelId = panel.getId();
46914         this.panels.removeKey(panelId);
46915         return panel;
46916     },
46917     
46918     /**
46919      * Returns the panel specified or null if it's not in this region.
46920      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
46921      * @return {Roo.ContentPanel}
46922      */
46923     getPanel : function(id){
46924         if(typeof id == "object"){ // must be panel obj
46925             return id;
46926         }
46927         return this.panels.get(id);
46928     },
46929     
46930     /**
46931      * Returns this regions position (north/south/east/west/center).
46932      * @return {String} 
46933      */
46934     getPosition: function(){
46935         return this.position;    
46936     }
46937 });/*
46938  * Based on:
46939  * Ext JS Library 1.1.1
46940  * Copyright(c) 2006-2007, Ext JS, LLC.
46941  *
46942  * Originally Released Under LGPL - original licence link has changed is not relivant.
46943  *
46944  * Fork - LGPL
46945  * <script type="text/javascript">
46946  */
46947  
46948 /**
46949  * @class Roo.LayoutRegion
46950  * @extends Roo.BasicLayoutRegion
46951  * This class represents a region in a layout manager.
46952  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
46953  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
46954  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
46955  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
46956  * @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})
46957  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
46958  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
46959  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
46960  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
46961  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
46962  * @cfg {String}    title           The title for the region (overrides panel titles)
46963  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
46964  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
46965  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
46966  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
46967  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
46968  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
46969  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
46970  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
46971  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
46972  * @cfg {Boolean}   showPin         True to show a pin button
46973  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
46974  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
46975  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
46976  * @cfg {Number}    width           For East/West panels
46977  * @cfg {Number}    height          For North/South panels
46978  * @cfg {Boolean}   split           To show the splitter
46979  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
46980  */
46981 Roo.LayoutRegion = function(mgr, config, pos){
46982     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
46983     var dh = Roo.DomHelper;
46984     /** This region's container element 
46985     * @type Roo.Element */
46986     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
46987     /** This region's title element 
46988     * @type Roo.Element */
46989
46990     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
46991         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
46992         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
46993     ]}, true);
46994     this.titleEl.enableDisplayMode();
46995     /** This region's title text element 
46996     * @type HTMLElement */
46997     this.titleTextEl = this.titleEl.dom.firstChild;
46998     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
46999     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
47000     this.closeBtn.enableDisplayMode();
47001     this.closeBtn.on("click", this.closeClicked, this);
47002     this.closeBtn.hide();
47003
47004     this.createBody(config);
47005     this.visible = true;
47006     this.collapsed = false;
47007
47008     if(config.hideWhenEmpty){
47009         this.hide();
47010         this.on("paneladded", this.validateVisibility, this);
47011         this.on("panelremoved", this.validateVisibility, this);
47012     }
47013     this.applyConfig(config);
47014 };
47015
47016 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
47017
47018     createBody : function(){
47019         /** This region's body element 
47020         * @type Roo.Element */
47021         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
47022     },
47023
47024     applyConfig : function(c){
47025         if(c.collapsible && this.position != "center" && !this.collapsedEl){
47026             var dh = Roo.DomHelper;
47027             if(c.titlebar !== false){
47028                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
47029                 this.collapseBtn.on("click", this.collapse, this);
47030                 this.collapseBtn.enableDisplayMode();
47031
47032                 if(c.showPin === true || this.showPin){
47033                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
47034                     this.stickBtn.enableDisplayMode();
47035                     this.stickBtn.on("click", this.expand, this);
47036                     this.stickBtn.hide();
47037                 }
47038             }
47039             /** This region's collapsed element
47040             * @type Roo.Element */
47041             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
47042                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
47043             ]}, true);
47044             if(c.floatable !== false){
47045                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
47046                this.collapsedEl.on("click", this.collapseClick, this);
47047             }
47048
47049             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
47050                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
47051                    id: "message", unselectable: "on", style:{"float":"left"}});
47052                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
47053              }
47054             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
47055             this.expandBtn.on("click", this.expand, this);
47056         }
47057         if(this.collapseBtn){
47058             this.collapseBtn.setVisible(c.collapsible == true);
47059         }
47060         this.cmargins = c.cmargins || this.cmargins ||
47061                          (this.position == "west" || this.position == "east" ?
47062                              {top: 0, left: 2, right:2, bottom: 0} :
47063                              {top: 2, left: 0, right:0, bottom: 2});
47064         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
47065         this.bottomTabs = c.tabPosition != "top";
47066         this.autoScroll = c.autoScroll || false;
47067         if(this.autoScroll){
47068             this.bodyEl.setStyle("overflow", "auto");
47069         }else{
47070             this.bodyEl.setStyle("overflow", "hidden");
47071         }
47072         //if(c.titlebar !== false){
47073             if((!c.titlebar && !c.title) || c.titlebar === false){
47074                 this.titleEl.hide();
47075             }else{
47076                 this.titleEl.show();
47077                 if(c.title){
47078                     this.titleTextEl.innerHTML = c.title;
47079                 }
47080             }
47081         //}
47082         this.duration = c.duration || .30;
47083         this.slideDuration = c.slideDuration || .45;
47084         this.config = c;
47085         if(c.collapsed){
47086             this.collapse(true);
47087         }
47088         if(c.hidden){
47089             this.hide();
47090         }
47091     },
47092     /**
47093      * Returns true if this region is currently visible.
47094      * @return {Boolean}
47095      */
47096     isVisible : function(){
47097         return this.visible;
47098     },
47099
47100     /**
47101      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
47102      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
47103      */
47104     setCollapsedTitle : function(title){
47105         title = title || "&#160;";
47106         if(this.collapsedTitleTextEl){
47107             this.collapsedTitleTextEl.innerHTML = title;
47108         }
47109     },
47110
47111     getBox : function(){
47112         var b;
47113         if(!this.collapsed){
47114             b = this.el.getBox(false, true);
47115         }else{
47116             b = this.collapsedEl.getBox(false, true);
47117         }
47118         return b;
47119     },
47120
47121     getMargins : function(){
47122         return this.collapsed ? this.cmargins : this.margins;
47123     },
47124
47125     highlight : function(){
47126         this.el.addClass("x-layout-panel-dragover");
47127     },
47128
47129     unhighlight : function(){
47130         this.el.removeClass("x-layout-panel-dragover");
47131     },
47132
47133     updateBox : function(box){
47134         this.box = box;
47135         if(!this.collapsed){
47136             this.el.dom.style.left = box.x + "px";
47137             this.el.dom.style.top = box.y + "px";
47138             this.updateBody(box.width, box.height);
47139         }else{
47140             this.collapsedEl.dom.style.left = box.x + "px";
47141             this.collapsedEl.dom.style.top = box.y + "px";
47142             this.collapsedEl.setSize(box.width, box.height);
47143         }
47144         if(this.tabs){
47145             this.tabs.autoSizeTabs();
47146         }
47147     },
47148
47149     updateBody : function(w, h){
47150         if(w !== null){
47151             this.el.setWidth(w);
47152             w -= this.el.getBorderWidth("rl");
47153             if(this.config.adjustments){
47154                 w += this.config.adjustments[0];
47155             }
47156         }
47157         if(h !== null){
47158             this.el.setHeight(h);
47159             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
47160             h -= this.el.getBorderWidth("tb");
47161             if(this.config.adjustments){
47162                 h += this.config.adjustments[1];
47163             }
47164             this.bodyEl.setHeight(h);
47165             if(this.tabs){
47166                 h = this.tabs.syncHeight(h);
47167             }
47168         }
47169         if(this.panelSize){
47170             w = w !== null ? w : this.panelSize.width;
47171             h = h !== null ? h : this.panelSize.height;
47172         }
47173         if(this.activePanel){
47174             var el = this.activePanel.getEl();
47175             w = w !== null ? w : el.getWidth();
47176             h = h !== null ? h : el.getHeight();
47177             this.panelSize = {width: w, height: h};
47178             this.activePanel.setSize(w, h);
47179         }
47180         if(Roo.isIE && this.tabs){
47181             this.tabs.el.repaint();
47182         }
47183     },
47184
47185     /**
47186      * Returns the container element for this region.
47187      * @return {Roo.Element}
47188      */
47189     getEl : function(){
47190         return this.el;
47191     },
47192
47193     /**
47194      * Hides this region.
47195      */
47196     hide : function(){
47197         if(!this.collapsed){
47198             this.el.dom.style.left = "-2000px";
47199             this.el.hide();
47200         }else{
47201             this.collapsedEl.dom.style.left = "-2000px";
47202             this.collapsedEl.hide();
47203         }
47204         this.visible = false;
47205         this.fireEvent("visibilitychange", this, false);
47206     },
47207
47208     /**
47209      * Shows this region if it was previously hidden.
47210      */
47211     show : function(){
47212         if(!this.collapsed){
47213             this.el.show();
47214         }else{
47215             this.collapsedEl.show();
47216         }
47217         this.visible = true;
47218         this.fireEvent("visibilitychange", this, true);
47219     },
47220
47221     closeClicked : function(){
47222         if(this.activePanel){
47223             this.remove(this.activePanel);
47224         }
47225     },
47226
47227     collapseClick : function(e){
47228         if(this.isSlid){
47229            e.stopPropagation();
47230            this.slideIn();
47231         }else{
47232            e.stopPropagation();
47233            this.slideOut();
47234         }
47235     },
47236
47237     /**
47238      * Collapses this region.
47239      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
47240      */
47241     collapse : function(skipAnim){
47242         if(this.collapsed) return;
47243         this.collapsed = true;
47244         if(this.split){
47245             this.split.el.hide();
47246         }
47247         if(this.config.animate && skipAnim !== true){
47248             this.fireEvent("invalidated", this);
47249             this.animateCollapse();
47250         }else{
47251             this.el.setLocation(-20000,-20000);
47252             this.el.hide();
47253             this.collapsedEl.show();
47254             this.fireEvent("collapsed", this);
47255             this.fireEvent("invalidated", this);
47256         }
47257     },
47258
47259     animateCollapse : function(){
47260         // overridden
47261     },
47262
47263     /**
47264      * Expands this region if it was previously collapsed.
47265      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
47266      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
47267      */
47268     expand : function(e, skipAnim){
47269         if(e) e.stopPropagation();
47270         if(!this.collapsed || this.el.hasActiveFx()) return;
47271         if(this.isSlid){
47272             this.afterSlideIn();
47273             skipAnim = true;
47274         }
47275         this.collapsed = false;
47276         if(this.config.animate && skipAnim !== true){
47277             this.animateExpand();
47278         }else{
47279             this.el.show();
47280             if(this.split){
47281                 this.split.el.show();
47282             }
47283             this.collapsedEl.setLocation(-2000,-2000);
47284             this.collapsedEl.hide();
47285             this.fireEvent("invalidated", this);
47286             this.fireEvent("expanded", this);
47287         }
47288     },
47289
47290     animateExpand : function(){
47291         // overridden
47292     },
47293
47294     initTabs : function()
47295     {
47296         this.bodyEl.setStyle("overflow", "hidden");
47297         var ts = new Roo.TabPanel(
47298                 this.bodyEl.dom,
47299                 {
47300                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
47301                     disableTooltips: this.config.disableTabTips,
47302                     toolbar : this.config.toolbar
47303                 }
47304         );
47305         if(this.config.hideTabs){
47306             ts.stripWrap.setDisplayed(false);
47307         }
47308         this.tabs = ts;
47309         ts.resizeTabs = this.config.resizeTabs === true;
47310         ts.minTabWidth = this.config.minTabWidth || 40;
47311         ts.maxTabWidth = this.config.maxTabWidth || 250;
47312         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
47313         ts.monitorResize = false;
47314         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
47315         ts.bodyEl.addClass('x-layout-tabs-body');
47316         this.panels.each(this.initPanelAsTab, this);
47317     },
47318
47319     initPanelAsTab : function(panel){
47320         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
47321                     this.config.closeOnTab && panel.isClosable());
47322         if(panel.tabTip !== undefined){
47323             ti.setTooltip(panel.tabTip);
47324         }
47325         ti.on("activate", function(){
47326               this.setActivePanel(panel);
47327         }, this);
47328         if(this.config.closeOnTab){
47329             ti.on("beforeclose", function(t, e){
47330                 e.cancel = true;
47331                 this.remove(panel);
47332             }, this);
47333         }
47334         return ti;
47335     },
47336
47337     updatePanelTitle : function(panel, title){
47338         if(this.activePanel == panel){
47339             this.updateTitle(title);
47340         }
47341         if(this.tabs){
47342             var ti = this.tabs.getTab(panel.getEl().id);
47343             ti.setText(title);
47344             if(panel.tabTip !== undefined){
47345                 ti.setTooltip(panel.tabTip);
47346             }
47347         }
47348     },
47349
47350     updateTitle : function(title){
47351         if(this.titleTextEl && !this.config.title){
47352             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
47353         }
47354     },
47355
47356     setActivePanel : function(panel){
47357         panel = this.getPanel(panel);
47358         if(this.activePanel && this.activePanel != panel){
47359             this.activePanel.setActiveState(false);
47360         }
47361         this.activePanel = panel;
47362         panel.setActiveState(true);
47363         if(this.panelSize){
47364             panel.setSize(this.panelSize.width, this.panelSize.height);
47365         }
47366         if(this.closeBtn){
47367             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
47368         }
47369         this.updateTitle(panel.getTitle());
47370         if(this.tabs){
47371             this.fireEvent("invalidated", this);
47372         }
47373         this.fireEvent("panelactivated", this, panel);
47374     },
47375
47376     /**
47377      * Shows the specified panel.
47378      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
47379      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
47380      */
47381     showPanel : function(panel){
47382         if(panel = this.getPanel(panel)){
47383             if(this.tabs){
47384                 var tab = this.tabs.getTab(panel.getEl().id);
47385                 if(tab.isHidden()){
47386                     this.tabs.unhideTab(tab.id);
47387                 }
47388                 tab.activate();
47389             }else{
47390                 this.setActivePanel(panel);
47391             }
47392         }
47393         return panel;
47394     },
47395
47396     /**
47397      * Get the active panel for this region.
47398      * @return {Roo.ContentPanel} The active panel or null
47399      */
47400     getActivePanel : function(){
47401         return this.activePanel;
47402     },
47403
47404     validateVisibility : function(){
47405         if(this.panels.getCount() < 1){
47406             this.updateTitle("&#160;");
47407             this.closeBtn.hide();
47408             this.hide();
47409         }else{
47410             if(!this.isVisible()){
47411                 this.show();
47412             }
47413         }
47414     },
47415
47416     /**
47417      * Adds the passed ContentPanel(s) to this region.
47418      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
47419      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
47420      */
47421     add : function(panel){
47422         if(arguments.length > 1){
47423             for(var i = 0, len = arguments.length; i < len; i++) {
47424                 this.add(arguments[i]);
47425             }
47426             return null;
47427         }
47428         if(this.hasPanel(panel)){
47429             this.showPanel(panel);
47430             return panel;
47431         }
47432         panel.setRegion(this);
47433         this.panels.add(panel);
47434         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
47435             this.bodyEl.dom.appendChild(panel.getEl().dom);
47436             if(panel.background !== true){
47437                 this.setActivePanel(panel);
47438             }
47439             this.fireEvent("paneladded", this, panel);
47440             return panel;
47441         }
47442         if(!this.tabs){
47443             this.initTabs();
47444         }else{
47445             this.initPanelAsTab(panel);
47446         }
47447         if(panel.background !== true){
47448             this.tabs.activate(panel.getEl().id);
47449         }
47450         this.fireEvent("paneladded", this, panel);
47451         return panel;
47452     },
47453
47454     /**
47455      * Hides the tab for the specified panel.
47456      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
47457      */
47458     hidePanel : function(panel){
47459         if(this.tabs && (panel = this.getPanel(panel))){
47460             this.tabs.hideTab(panel.getEl().id);
47461         }
47462     },
47463
47464     /**
47465      * Unhides the tab for a previously hidden panel.
47466      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
47467      */
47468     unhidePanel : function(panel){
47469         if(this.tabs && (panel = this.getPanel(panel))){
47470             this.tabs.unhideTab(panel.getEl().id);
47471         }
47472     },
47473
47474     clearPanels : function(){
47475         while(this.panels.getCount() > 0){
47476              this.remove(this.panels.first());
47477         }
47478     },
47479
47480     /**
47481      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
47482      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
47483      * @param {Boolean} preservePanel Overrides the config preservePanel option
47484      * @return {Roo.ContentPanel} The panel that was removed
47485      */
47486     remove : function(panel, preservePanel){
47487         panel = this.getPanel(panel);
47488         if(!panel){
47489             return null;
47490         }
47491         var e = {};
47492         this.fireEvent("beforeremove", this, panel, e);
47493         if(e.cancel === true){
47494             return null;
47495         }
47496         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
47497         var panelId = panel.getId();
47498         this.panels.removeKey(panelId);
47499         if(preservePanel){
47500             document.body.appendChild(panel.getEl().dom);
47501         }
47502         if(this.tabs){
47503             this.tabs.removeTab(panel.getEl().id);
47504         }else if (!preservePanel){
47505             this.bodyEl.dom.removeChild(panel.getEl().dom);
47506         }
47507         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
47508             var p = this.panels.first();
47509             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
47510             tempEl.appendChild(p.getEl().dom);
47511             this.bodyEl.update("");
47512             this.bodyEl.dom.appendChild(p.getEl().dom);
47513             tempEl = null;
47514             this.updateTitle(p.getTitle());
47515             this.tabs = null;
47516             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
47517             this.setActivePanel(p);
47518         }
47519         panel.setRegion(null);
47520         if(this.activePanel == panel){
47521             this.activePanel = null;
47522         }
47523         if(this.config.autoDestroy !== false && preservePanel !== true){
47524             try{panel.destroy();}catch(e){}
47525         }
47526         this.fireEvent("panelremoved", this, panel);
47527         return panel;
47528     },
47529
47530     /**
47531      * Returns the TabPanel component used by this region
47532      * @return {Roo.TabPanel}
47533      */
47534     getTabs : function(){
47535         return this.tabs;
47536     },
47537
47538     createTool : function(parentEl, className){
47539         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
47540             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
47541         btn.addClassOnOver("x-layout-tools-button-over");
47542         return btn;
47543     }
47544 });/*
47545  * Based on:
47546  * Ext JS Library 1.1.1
47547  * Copyright(c) 2006-2007, Ext JS, LLC.
47548  *
47549  * Originally Released Under LGPL - original licence link has changed is not relivant.
47550  *
47551  * Fork - LGPL
47552  * <script type="text/javascript">
47553  */
47554  
47555
47556
47557 /**
47558  * @class Roo.SplitLayoutRegion
47559  * @extends Roo.LayoutRegion
47560  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
47561  */
47562 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
47563     this.cursor = cursor;
47564     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
47565 };
47566
47567 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
47568     splitTip : "Drag to resize.",
47569     collapsibleSplitTip : "Drag to resize. Double click to hide.",
47570     useSplitTips : false,
47571
47572     applyConfig : function(config){
47573         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
47574         if(config.split){
47575             if(!this.split){
47576                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
47577                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
47578                 /** The SplitBar for this region 
47579                 * @type Roo.SplitBar */
47580                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
47581                 this.split.on("moved", this.onSplitMove, this);
47582                 this.split.useShim = config.useShim === true;
47583                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
47584                 if(this.useSplitTips){
47585                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
47586                 }
47587                 if(config.collapsible){
47588                     this.split.el.on("dblclick", this.collapse,  this);
47589                 }
47590             }
47591             if(typeof config.minSize != "undefined"){
47592                 this.split.minSize = config.minSize;
47593             }
47594             if(typeof config.maxSize != "undefined"){
47595                 this.split.maxSize = config.maxSize;
47596             }
47597             if(config.hideWhenEmpty || config.hidden || config.collapsed){
47598                 this.hideSplitter();
47599             }
47600         }
47601     },
47602
47603     getHMaxSize : function(){
47604          var cmax = this.config.maxSize || 10000;
47605          var center = this.mgr.getRegion("center");
47606          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
47607     },
47608
47609     getVMaxSize : function(){
47610          var cmax = this.config.maxSize || 10000;
47611          var center = this.mgr.getRegion("center");
47612          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
47613     },
47614
47615     onSplitMove : function(split, newSize){
47616         this.fireEvent("resized", this, newSize);
47617     },
47618     
47619     /** 
47620      * Returns the {@link Roo.SplitBar} for this region.
47621      * @return {Roo.SplitBar}
47622      */
47623     getSplitBar : function(){
47624         return this.split;
47625     },
47626     
47627     hide : function(){
47628         this.hideSplitter();
47629         Roo.SplitLayoutRegion.superclass.hide.call(this);
47630     },
47631
47632     hideSplitter : function(){
47633         if(this.split){
47634             this.split.el.setLocation(-2000,-2000);
47635             this.split.el.hide();
47636         }
47637     },
47638
47639     show : function(){
47640         if(this.split){
47641             this.split.el.show();
47642         }
47643         Roo.SplitLayoutRegion.superclass.show.call(this);
47644     },
47645     
47646     beforeSlide: function(){
47647         if(Roo.isGecko){// firefox overflow auto bug workaround
47648             this.bodyEl.clip();
47649             if(this.tabs) this.tabs.bodyEl.clip();
47650             if(this.activePanel){
47651                 this.activePanel.getEl().clip();
47652                 
47653                 if(this.activePanel.beforeSlide){
47654                     this.activePanel.beforeSlide();
47655                 }
47656             }
47657         }
47658     },
47659     
47660     afterSlide : function(){
47661         if(Roo.isGecko){// firefox overflow auto bug workaround
47662             this.bodyEl.unclip();
47663             if(this.tabs) this.tabs.bodyEl.unclip();
47664             if(this.activePanel){
47665                 this.activePanel.getEl().unclip();
47666                 if(this.activePanel.afterSlide){
47667                     this.activePanel.afterSlide();
47668                 }
47669             }
47670         }
47671     },
47672
47673     initAutoHide : function(){
47674         if(this.autoHide !== false){
47675             if(!this.autoHideHd){
47676                 var st = new Roo.util.DelayedTask(this.slideIn, this);
47677                 this.autoHideHd = {
47678                     "mouseout": function(e){
47679                         if(!e.within(this.el, true)){
47680                             st.delay(500);
47681                         }
47682                     },
47683                     "mouseover" : function(e){
47684                         st.cancel();
47685                     },
47686                     scope : this
47687                 };
47688             }
47689             this.el.on(this.autoHideHd);
47690         }
47691     },
47692
47693     clearAutoHide : function(){
47694         if(this.autoHide !== false){
47695             this.el.un("mouseout", this.autoHideHd.mouseout);
47696             this.el.un("mouseover", this.autoHideHd.mouseover);
47697         }
47698     },
47699
47700     clearMonitor : function(){
47701         Roo.get(document).un("click", this.slideInIf, this);
47702     },
47703
47704     // these names are backwards but not changed for compat
47705     slideOut : function(){
47706         if(this.isSlid || this.el.hasActiveFx()){
47707             return;
47708         }
47709         this.isSlid = true;
47710         if(this.collapseBtn){
47711             this.collapseBtn.hide();
47712         }
47713         this.closeBtnState = this.closeBtn.getStyle('display');
47714         this.closeBtn.hide();
47715         if(this.stickBtn){
47716             this.stickBtn.show();
47717         }
47718         this.el.show();
47719         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
47720         this.beforeSlide();
47721         this.el.setStyle("z-index", 10001);
47722         this.el.slideIn(this.getSlideAnchor(), {
47723             callback: function(){
47724                 this.afterSlide();
47725                 this.initAutoHide();
47726                 Roo.get(document).on("click", this.slideInIf, this);
47727                 this.fireEvent("slideshow", this);
47728             },
47729             scope: this,
47730             block: true
47731         });
47732     },
47733
47734     afterSlideIn : function(){
47735         this.clearAutoHide();
47736         this.isSlid = false;
47737         this.clearMonitor();
47738         this.el.setStyle("z-index", "");
47739         if(this.collapseBtn){
47740             this.collapseBtn.show();
47741         }
47742         this.closeBtn.setStyle('display', this.closeBtnState);
47743         if(this.stickBtn){
47744             this.stickBtn.hide();
47745         }
47746         this.fireEvent("slidehide", this);
47747     },
47748
47749     slideIn : function(cb){
47750         if(!this.isSlid || this.el.hasActiveFx()){
47751             Roo.callback(cb);
47752             return;
47753         }
47754         this.isSlid = false;
47755         this.beforeSlide();
47756         this.el.slideOut(this.getSlideAnchor(), {
47757             callback: function(){
47758                 this.el.setLeftTop(-10000, -10000);
47759                 this.afterSlide();
47760                 this.afterSlideIn();
47761                 Roo.callback(cb);
47762             },
47763             scope: this,
47764             block: true
47765         });
47766     },
47767     
47768     slideInIf : function(e){
47769         if(!e.within(this.el)){
47770             this.slideIn();
47771         }
47772     },
47773
47774     animateCollapse : function(){
47775         this.beforeSlide();
47776         this.el.setStyle("z-index", 20000);
47777         var anchor = this.getSlideAnchor();
47778         this.el.slideOut(anchor, {
47779             callback : function(){
47780                 this.el.setStyle("z-index", "");
47781                 this.collapsedEl.slideIn(anchor, {duration:.3});
47782                 this.afterSlide();
47783                 this.el.setLocation(-10000,-10000);
47784                 this.el.hide();
47785                 this.fireEvent("collapsed", this);
47786             },
47787             scope: this,
47788             block: true
47789         });
47790     },
47791
47792     animateExpand : function(){
47793         this.beforeSlide();
47794         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
47795         this.el.setStyle("z-index", 20000);
47796         this.collapsedEl.hide({
47797             duration:.1
47798         });
47799         this.el.slideIn(this.getSlideAnchor(), {
47800             callback : function(){
47801                 this.el.setStyle("z-index", "");
47802                 this.afterSlide();
47803                 if(this.split){
47804                     this.split.el.show();
47805                 }
47806                 this.fireEvent("invalidated", this);
47807                 this.fireEvent("expanded", this);
47808             },
47809             scope: this,
47810             block: true
47811         });
47812     },
47813
47814     anchors : {
47815         "west" : "left",
47816         "east" : "right",
47817         "north" : "top",
47818         "south" : "bottom"
47819     },
47820
47821     sanchors : {
47822         "west" : "l",
47823         "east" : "r",
47824         "north" : "t",
47825         "south" : "b"
47826     },
47827
47828     canchors : {
47829         "west" : "tl-tr",
47830         "east" : "tr-tl",
47831         "north" : "tl-bl",
47832         "south" : "bl-tl"
47833     },
47834
47835     getAnchor : function(){
47836         return this.anchors[this.position];
47837     },
47838
47839     getCollapseAnchor : function(){
47840         return this.canchors[this.position];
47841     },
47842
47843     getSlideAnchor : function(){
47844         return this.sanchors[this.position];
47845     },
47846
47847     getAlignAdj : function(){
47848         var cm = this.cmargins;
47849         switch(this.position){
47850             case "west":
47851                 return [0, 0];
47852             break;
47853             case "east":
47854                 return [0, 0];
47855             break;
47856             case "north":
47857                 return [0, 0];
47858             break;
47859             case "south":
47860                 return [0, 0];
47861             break;
47862         }
47863     },
47864
47865     getExpandAdj : function(){
47866         var c = this.collapsedEl, cm = this.cmargins;
47867         switch(this.position){
47868             case "west":
47869                 return [-(cm.right+c.getWidth()+cm.left), 0];
47870             break;
47871             case "east":
47872                 return [cm.right+c.getWidth()+cm.left, 0];
47873             break;
47874             case "north":
47875                 return [0, -(cm.top+cm.bottom+c.getHeight())];
47876             break;
47877             case "south":
47878                 return [0, cm.top+cm.bottom+c.getHeight()];
47879             break;
47880         }
47881     }
47882 });/*
47883  * Based on:
47884  * Ext JS Library 1.1.1
47885  * Copyright(c) 2006-2007, Ext JS, LLC.
47886  *
47887  * Originally Released Under LGPL - original licence link has changed is not relivant.
47888  *
47889  * Fork - LGPL
47890  * <script type="text/javascript">
47891  */
47892 /*
47893  * These classes are private internal classes
47894  */
47895 Roo.CenterLayoutRegion = function(mgr, config){
47896     Roo.LayoutRegion.call(this, mgr, config, "center");
47897     this.visible = true;
47898     this.minWidth = config.minWidth || 20;
47899     this.minHeight = config.minHeight || 20;
47900 };
47901
47902 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
47903     hide : function(){
47904         // center panel can't be hidden
47905     },
47906     
47907     show : function(){
47908         // center panel can't be hidden
47909     },
47910     
47911     getMinWidth: function(){
47912         return this.minWidth;
47913     },
47914     
47915     getMinHeight: function(){
47916         return this.minHeight;
47917     }
47918 });
47919
47920
47921 Roo.NorthLayoutRegion = function(mgr, config){
47922     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
47923     if(this.split){
47924         this.split.placement = Roo.SplitBar.TOP;
47925         this.split.orientation = Roo.SplitBar.VERTICAL;
47926         this.split.el.addClass("x-layout-split-v");
47927     }
47928     var size = config.initialSize || config.height;
47929     if(typeof size != "undefined"){
47930         this.el.setHeight(size);
47931     }
47932 };
47933 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
47934     orientation: Roo.SplitBar.VERTICAL,
47935     getBox : function(){
47936         if(this.collapsed){
47937             return this.collapsedEl.getBox();
47938         }
47939         var box = this.el.getBox();
47940         if(this.split){
47941             box.height += this.split.el.getHeight();
47942         }
47943         return box;
47944     },
47945     
47946     updateBox : function(box){
47947         if(this.split && !this.collapsed){
47948             box.height -= this.split.el.getHeight();
47949             this.split.el.setLeft(box.x);
47950             this.split.el.setTop(box.y+box.height);
47951             this.split.el.setWidth(box.width);
47952         }
47953         if(this.collapsed){
47954             this.updateBody(box.width, null);
47955         }
47956         Roo.LayoutRegion.prototype.updateBox.call(this, box);
47957     }
47958 });
47959
47960 Roo.SouthLayoutRegion = function(mgr, config){
47961     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
47962     if(this.split){
47963         this.split.placement = Roo.SplitBar.BOTTOM;
47964         this.split.orientation = Roo.SplitBar.VERTICAL;
47965         this.split.el.addClass("x-layout-split-v");
47966     }
47967     var size = config.initialSize || config.height;
47968     if(typeof size != "undefined"){
47969         this.el.setHeight(size);
47970     }
47971 };
47972 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
47973     orientation: Roo.SplitBar.VERTICAL,
47974     getBox : function(){
47975         if(this.collapsed){
47976             return this.collapsedEl.getBox();
47977         }
47978         var box = this.el.getBox();
47979         if(this.split){
47980             var sh = this.split.el.getHeight();
47981             box.height += sh;
47982             box.y -= sh;
47983         }
47984         return box;
47985     },
47986     
47987     updateBox : function(box){
47988         if(this.split && !this.collapsed){
47989             var sh = this.split.el.getHeight();
47990             box.height -= sh;
47991             box.y += sh;
47992             this.split.el.setLeft(box.x);
47993             this.split.el.setTop(box.y-sh);
47994             this.split.el.setWidth(box.width);
47995         }
47996         if(this.collapsed){
47997             this.updateBody(box.width, null);
47998         }
47999         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48000     }
48001 });
48002
48003 Roo.EastLayoutRegion = function(mgr, config){
48004     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
48005     if(this.split){
48006         this.split.placement = Roo.SplitBar.RIGHT;
48007         this.split.orientation = Roo.SplitBar.HORIZONTAL;
48008         this.split.el.addClass("x-layout-split-h");
48009     }
48010     var size = config.initialSize || config.width;
48011     if(typeof size != "undefined"){
48012         this.el.setWidth(size);
48013     }
48014 };
48015 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
48016     orientation: Roo.SplitBar.HORIZONTAL,
48017     getBox : function(){
48018         if(this.collapsed){
48019             return this.collapsedEl.getBox();
48020         }
48021         var box = this.el.getBox();
48022         if(this.split){
48023             var sw = this.split.el.getWidth();
48024             box.width += sw;
48025             box.x -= sw;
48026         }
48027         return box;
48028     },
48029
48030     updateBox : function(box){
48031         if(this.split && !this.collapsed){
48032             var sw = this.split.el.getWidth();
48033             box.width -= sw;
48034             this.split.el.setLeft(box.x);
48035             this.split.el.setTop(box.y);
48036             this.split.el.setHeight(box.height);
48037             box.x += sw;
48038         }
48039         if(this.collapsed){
48040             this.updateBody(null, box.height);
48041         }
48042         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48043     }
48044 });
48045
48046 Roo.WestLayoutRegion = function(mgr, config){
48047     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
48048     if(this.split){
48049         this.split.placement = Roo.SplitBar.LEFT;
48050         this.split.orientation = Roo.SplitBar.HORIZONTAL;
48051         this.split.el.addClass("x-layout-split-h");
48052     }
48053     var size = config.initialSize || config.width;
48054     if(typeof size != "undefined"){
48055         this.el.setWidth(size);
48056     }
48057 };
48058 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
48059     orientation: Roo.SplitBar.HORIZONTAL,
48060     getBox : function(){
48061         if(this.collapsed){
48062             return this.collapsedEl.getBox();
48063         }
48064         var box = this.el.getBox();
48065         if(this.split){
48066             box.width += this.split.el.getWidth();
48067         }
48068         return box;
48069     },
48070     
48071     updateBox : function(box){
48072         if(this.split && !this.collapsed){
48073             var sw = this.split.el.getWidth();
48074             box.width -= sw;
48075             this.split.el.setLeft(box.x+box.width);
48076             this.split.el.setTop(box.y);
48077             this.split.el.setHeight(box.height);
48078         }
48079         if(this.collapsed){
48080             this.updateBody(null, box.height);
48081         }
48082         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48083     }
48084 });
48085 /*
48086  * Based on:
48087  * Ext JS Library 1.1.1
48088  * Copyright(c) 2006-2007, Ext JS, LLC.
48089  *
48090  * Originally Released Under LGPL - original licence link has changed is not relivant.
48091  *
48092  * Fork - LGPL
48093  * <script type="text/javascript">
48094  */
48095  
48096  
48097 /*
48098  * Private internal class for reading and applying state
48099  */
48100 Roo.LayoutStateManager = function(layout){
48101      // default empty state
48102      this.state = {
48103         north: {},
48104         south: {},
48105         east: {},
48106         west: {}       
48107     };
48108 };
48109
48110 Roo.LayoutStateManager.prototype = {
48111     init : function(layout, provider){
48112         this.provider = provider;
48113         var state = provider.get(layout.id+"-layout-state");
48114         if(state){
48115             var wasUpdating = layout.isUpdating();
48116             if(!wasUpdating){
48117                 layout.beginUpdate();
48118             }
48119             for(var key in state){
48120                 if(typeof state[key] != "function"){
48121                     var rstate = state[key];
48122                     var r = layout.getRegion(key);
48123                     if(r && rstate){
48124                         if(rstate.size){
48125                             r.resizeTo(rstate.size);
48126                         }
48127                         if(rstate.collapsed == true){
48128                             r.collapse(true);
48129                         }else{
48130                             r.expand(null, true);
48131                         }
48132                     }
48133                 }
48134             }
48135             if(!wasUpdating){
48136                 layout.endUpdate();
48137             }
48138             this.state = state; 
48139         }
48140         this.layout = layout;
48141         layout.on("regionresized", this.onRegionResized, this);
48142         layout.on("regioncollapsed", this.onRegionCollapsed, this);
48143         layout.on("regionexpanded", this.onRegionExpanded, this);
48144     },
48145     
48146     storeState : function(){
48147         this.provider.set(this.layout.id+"-layout-state", this.state);
48148     },
48149     
48150     onRegionResized : function(region, newSize){
48151         this.state[region.getPosition()].size = newSize;
48152         this.storeState();
48153     },
48154     
48155     onRegionCollapsed : function(region){
48156         this.state[region.getPosition()].collapsed = true;
48157         this.storeState();
48158     },
48159     
48160     onRegionExpanded : function(region){
48161         this.state[region.getPosition()].collapsed = false;
48162         this.storeState();
48163     }
48164 };/*
48165  * Based on:
48166  * Ext JS Library 1.1.1
48167  * Copyright(c) 2006-2007, Ext JS, LLC.
48168  *
48169  * Originally Released Under LGPL - original licence link has changed is not relivant.
48170  *
48171  * Fork - LGPL
48172  * <script type="text/javascript">
48173  */
48174 /**
48175  * @class Roo.ContentPanel
48176  * @extends Roo.util.Observable
48177  * A basic ContentPanel element.
48178  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
48179  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
48180  * @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
48181  * @cfg {Boolean}   closable      True if the panel can be closed/removed
48182  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
48183  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
48184  * @cfg {Toolbar}   toolbar       A toolbar for this panel
48185  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
48186  * @cfg {String} title          The title for this panel
48187  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
48188  * @cfg {String} url            Calls {@link #setUrl} with this value
48189  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
48190  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
48191  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
48192  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
48193
48194  * @constructor
48195  * Create a new ContentPanel.
48196  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
48197  * @param {String/Object} config A string to set only the title or a config object
48198  * @param {String} content (optional) Set the HTML content for this panel
48199  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
48200  */
48201 Roo.ContentPanel = function(el, config, content){
48202     
48203      
48204     /*
48205     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
48206         config = el;
48207         el = Roo.id();
48208     }
48209     if (config && config.parentLayout) { 
48210         el = config.parentLayout.el.createChild(); 
48211     }
48212     */
48213     if(el.autoCreate){ // xtype is available if this is called from factory
48214         config = el;
48215         el = Roo.id();
48216     }
48217     this.el = Roo.get(el);
48218     if(!this.el && config && config.autoCreate){
48219         if(typeof config.autoCreate == "object"){
48220             if(!config.autoCreate.id){
48221                 config.autoCreate.id = config.id||el;
48222             }
48223             this.el = Roo.DomHelper.append(document.body,
48224                         config.autoCreate, true);
48225         }else{
48226             this.el = Roo.DomHelper.append(document.body,
48227                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
48228         }
48229     }
48230     this.closable = false;
48231     this.loaded = false;
48232     this.active = false;
48233     if(typeof config == "string"){
48234         this.title = config;
48235     }else{
48236         Roo.apply(this, config);
48237     }
48238     
48239     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
48240         this.wrapEl = this.el.wrap();
48241         this.toolbar.container = this.el.insertSibling(false, 'before');
48242         this.toolbar = new Roo.Toolbar(this.toolbar);
48243     }
48244     
48245     // xtype created footer. - not sure if will work as we normally have to render first..
48246     if (this.footer && !this.footer.el && this.footer.xtype) {
48247         if (!this.wrapEl) {
48248             this.wrapEl = this.el.wrap();
48249         }
48250     
48251         this.footer.container = this.wrapEl.createChild();
48252          
48253         this.footer = Roo.factory(this.footer, Roo);
48254         
48255     }
48256     
48257     if(this.resizeEl){
48258         this.resizeEl = Roo.get(this.resizeEl, true);
48259     }else{
48260         this.resizeEl = this.el;
48261     }
48262     // handle view.xtype
48263     
48264     if (this.view && typeof(this.view.xtype) != 'undefined') {
48265         this.view.el = this.el.appendChild(document.createElement("div"));
48266         this.view = Roo.factory(this.view);
48267         this.view.render && this.view.render(false, ''); // render blank..
48268     }
48269     
48270     
48271     
48272     this.addEvents({
48273         /**
48274          * @event activate
48275          * Fires when this panel is activated. 
48276          * @param {Roo.ContentPanel} this
48277          */
48278         "activate" : true,
48279         /**
48280          * @event deactivate
48281          * Fires when this panel is activated. 
48282          * @param {Roo.ContentPanel} this
48283          */
48284         "deactivate" : true,
48285
48286         /**
48287          * @event resize
48288          * Fires when this panel is resized if fitToFrame is true.
48289          * @param {Roo.ContentPanel} this
48290          * @param {Number} width The width after any component adjustments
48291          * @param {Number} height The height after any component adjustments
48292          */
48293         "resize" : true,
48294         
48295          /**
48296          * @event render
48297          * Fires when this tab is created
48298          * @param {Roo.ContentPanel} this
48299          */
48300         "render" : true
48301         
48302         
48303         
48304     });
48305     if(this.autoScroll){
48306         this.resizeEl.setStyle("overflow", "auto");
48307     } else {
48308         // fix randome scrolling
48309         this.el.on('scroll', function() {
48310             Roo.log('fix random scolling');
48311             this.scrollTo('top',0); 
48312         });
48313     }
48314     content = content || this.content;
48315     if(content){
48316         this.setContent(content);
48317     }
48318     if(config && config.url){
48319         this.setUrl(this.url, this.params, this.loadOnce);
48320     }
48321     
48322     
48323     
48324     Roo.ContentPanel.superclass.constructor.call(this);
48325     
48326     this.fireEvent('render', this);
48327 };
48328
48329 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
48330     tabTip:'',
48331     setRegion : function(region){
48332         this.region = region;
48333         if(region){
48334            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
48335         }else{
48336            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
48337         } 
48338     },
48339     
48340     /**
48341      * Returns the toolbar for this Panel if one was configured. 
48342      * @return {Roo.Toolbar} 
48343      */
48344     getToolbar : function(){
48345         return this.toolbar;
48346     },
48347     
48348     setActiveState : function(active){
48349         this.active = active;
48350         if(!active){
48351             this.fireEvent("deactivate", this);
48352         }else{
48353             this.fireEvent("activate", this);
48354         }
48355     },
48356     /**
48357      * Updates this panel's element
48358      * @param {String} content The new content
48359      * @param {Boolean} loadScripts (optional) true to look for and process scripts
48360     */
48361     setContent : function(content, loadScripts){
48362         this.el.update(content, loadScripts);
48363     },
48364
48365     ignoreResize : function(w, h){
48366         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
48367             return true;
48368         }else{
48369             this.lastSize = {width: w, height: h};
48370             return false;
48371         }
48372     },
48373     /**
48374      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
48375      * @return {Roo.UpdateManager} The UpdateManager
48376      */
48377     getUpdateManager : function(){
48378         return this.el.getUpdateManager();
48379     },
48380      /**
48381      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
48382      * @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:
48383 <pre><code>
48384 panel.load({
48385     url: "your-url.php",
48386     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
48387     callback: yourFunction,
48388     scope: yourObject, //(optional scope)
48389     discardUrl: false,
48390     nocache: false,
48391     text: "Loading...",
48392     timeout: 30,
48393     scripts: false
48394 });
48395 </code></pre>
48396      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
48397      * 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.
48398      * @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}
48399      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
48400      * @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.
48401      * @return {Roo.ContentPanel} this
48402      */
48403     load : function(){
48404         var um = this.el.getUpdateManager();
48405         um.update.apply(um, arguments);
48406         return this;
48407     },
48408
48409
48410     /**
48411      * 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.
48412      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
48413      * @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)
48414      * @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)
48415      * @return {Roo.UpdateManager} The UpdateManager
48416      */
48417     setUrl : function(url, params, loadOnce){
48418         if(this.refreshDelegate){
48419             this.removeListener("activate", this.refreshDelegate);
48420         }
48421         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
48422         this.on("activate", this.refreshDelegate);
48423         return this.el.getUpdateManager();
48424     },
48425     
48426     _handleRefresh : function(url, params, loadOnce){
48427         if(!loadOnce || !this.loaded){
48428             var updater = this.el.getUpdateManager();
48429             updater.update(url, params, this._setLoaded.createDelegate(this));
48430         }
48431     },
48432     
48433     _setLoaded : function(){
48434         this.loaded = true;
48435     }, 
48436     
48437     /**
48438      * Returns this panel's id
48439      * @return {String} 
48440      */
48441     getId : function(){
48442         return this.el.id;
48443     },
48444     
48445     /** 
48446      * Returns this panel's element - used by regiosn to add.
48447      * @return {Roo.Element} 
48448      */
48449     getEl : function(){
48450         return this.wrapEl || this.el;
48451     },
48452     
48453     adjustForComponents : function(width, height)
48454     {
48455         Roo.log('adjustForComponents ');
48456         if(this.resizeEl != this.el){
48457             width -= this.el.getFrameWidth('lr');
48458             height -= this.el.getFrameWidth('tb');
48459         }
48460         if(this.toolbar){
48461             var te = this.toolbar.getEl();
48462             height -= te.getHeight();
48463             te.setWidth(width);
48464         }
48465         if(this.footer){
48466             var te = this.footer.getEl();
48467             Roo.log("footer:" + te.getHeight());
48468             
48469             height -= te.getHeight();
48470             te.setWidth(width);
48471         }
48472         
48473         
48474         if(this.adjustments){
48475             width += this.adjustments[0];
48476             height += this.adjustments[1];
48477         }
48478         return {"width": width, "height": height};
48479     },
48480     
48481     setSize : function(width, height){
48482         if(this.fitToFrame && !this.ignoreResize(width, height)){
48483             if(this.fitContainer && this.resizeEl != this.el){
48484                 this.el.setSize(width, height);
48485             }
48486             var size = this.adjustForComponents(width, height);
48487             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
48488             this.fireEvent('resize', this, size.width, size.height);
48489         }
48490     },
48491     
48492     /**
48493      * Returns this panel's title
48494      * @return {String} 
48495      */
48496     getTitle : function(){
48497         return this.title;
48498     },
48499     
48500     /**
48501      * Set this panel's title
48502      * @param {String} title
48503      */
48504     setTitle : function(title){
48505         this.title = title;
48506         if(this.region){
48507             this.region.updatePanelTitle(this, title);
48508         }
48509     },
48510     
48511     /**
48512      * Returns true is this panel was configured to be closable
48513      * @return {Boolean} 
48514      */
48515     isClosable : function(){
48516         return this.closable;
48517     },
48518     
48519     beforeSlide : function(){
48520         this.el.clip();
48521         this.resizeEl.clip();
48522     },
48523     
48524     afterSlide : function(){
48525         this.el.unclip();
48526         this.resizeEl.unclip();
48527     },
48528     
48529     /**
48530      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
48531      *   Will fail silently if the {@link #setUrl} method has not been called.
48532      *   This does not activate the panel, just updates its content.
48533      */
48534     refresh : function(){
48535         if(this.refreshDelegate){
48536            this.loaded = false;
48537            this.refreshDelegate();
48538         }
48539     },
48540     
48541     /**
48542      * Destroys this panel
48543      */
48544     destroy : function(){
48545         this.el.removeAllListeners();
48546         var tempEl = document.createElement("span");
48547         tempEl.appendChild(this.el.dom);
48548         tempEl.innerHTML = "";
48549         this.el.remove();
48550         this.el = null;
48551     },
48552     
48553     /**
48554      * form - if the content panel contains a form - this is a reference to it.
48555      * @type {Roo.form.Form}
48556      */
48557     form : false,
48558     /**
48559      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
48560      *    This contains a reference to it.
48561      * @type {Roo.View}
48562      */
48563     view : false,
48564     
48565       /**
48566      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
48567      * <pre><code>
48568
48569 layout.addxtype({
48570        xtype : 'Form',
48571        items: [ .... ]
48572    }
48573 );
48574
48575 </code></pre>
48576      * @param {Object} cfg Xtype definition of item to add.
48577      */
48578     
48579     addxtype : function(cfg) {
48580         // add form..
48581         if (cfg.xtype.match(/^Form$/)) {
48582             
48583             var el;
48584             //if (this.footer) {
48585             //    el = this.footer.container.insertSibling(false, 'before');
48586             //} else {
48587                 el = this.el.createChild();
48588             //}
48589
48590             this.form = new  Roo.form.Form(cfg);
48591             
48592             
48593             if ( this.form.allItems.length) this.form.render(el.dom);
48594             return this.form;
48595         }
48596         // should only have one of theses..
48597         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
48598             // views..
48599             cfg.el = this.el.appendChild(document.createElement("div"));
48600             // factory?
48601             
48602             var ret = new Roo.factory(cfg);
48603             ret.render && ret.render(false, ''); // render blank..
48604             this.view = ret;
48605             return ret;
48606         }
48607         return false;
48608     }
48609 });
48610
48611 /**
48612  * @class Roo.GridPanel
48613  * @extends Roo.ContentPanel
48614  * @constructor
48615  * Create a new GridPanel.
48616  * @param {Roo.grid.Grid} grid The grid for this panel
48617  * @param {String/Object} config A string to set only the panel's title, or a config object
48618  */
48619 Roo.GridPanel = function(grid, config){
48620     
48621   
48622     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
48623         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
48624         
48625     this.wrapper.dom.appendChild(grid.getGridEl().dom);
48626     
48627     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
48628     
48629     if(this.toolbar){
48630         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
48631     }
48632     // xtype created footer. - not sure if will work as we normally have to render first..
48633     if (this.footer && !this.footer.el && this.footer.xtype) {
48634         
48635         this.footer.container = this.grid.getView().getFooterPanel(true);
48636         this.footer.dataSource = this.grid.dataSource;
48637         this.footer = Roo.factory(this.footer, Roo);
48638         
48639     }
48640     
48641     grid.monitorWindowResize = false; // turn off autosizing
48642     grid.autoHeight = false;
48643     grid.autoWidth = false;
48644     this.grid = grid;
48645     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
48646 };
48647
48648 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
48649     getId : function(){
48650         return this.grid.id;
48651     },
48652     
48653     /**
48654      * Returns the grid for this panel
48655      * @return {Roo.grid.Grid} 
48656      */
48657     getGrid : function(){
48658         return this.grid;    
48659     },
48660     
48661     setSize : function(width, height){
48662         if(!this.ignoreResize(width, height)){
48663             var grid = this.grid;
48664             var size = this.adjustForComponents(width, height);
48665             grid.getGridEl().setSize(size.width, size.height);
48666             grid.autoSize();
48667         }
48668     },
48669     
48670     beforeSlide : function(){
48671         this.grid.getView().scroller.clip();
48672     },
48673     
48674     afterSlide : function(){
48675         this.grid.getView().scroller.unclip();
48676     },
48677     
48678     destroy : function(){
48679         this.grid.destroy();
48680         delete this.grid;
48681         Roo.GridPanel.superclass.destroy.call(this); 
48682     }
48683 });
48684
48685
48686 /**
48687  * @class Roo.NestedLayoutPanel
48688  * @extends Roo.ContentPanel
48689  * @constructor
48690  * Create a new NestedLayoutPanel.
48691  * 
48692  * 
48693  * @param {Roo.BorderLayout} layout The layout for this panel
48694  * @param {String/Object} config A string to set only the title or a config object
48695  */
48696 Roo.NestedLayoutPanel = function(layout, config)
48697 {
48698     // construct with only one argument..
48699     /* FIXME - implement nicer consturctors
48700     if (layout.layout) {
48701         config = layout;
48702         layout = config.layout;
48703         delete config.layout;
48704     }
48705     if (layout.xtype && !layout.getEl) {
48706         // then layout needs constructing..
48707         layout = Roo.factory(layout, Roo);
48708     }
48709     */
48710     
48711     
48712     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
48713     
48714     layout.monitorWindowResize = false; // turn off autosizing
48715     this.layout = layout;
48716     this.layout.getEl().addClass("x-layout-nested-layout");
48717     
48718     
48719     
48720     
48721 };
48722
48723 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
48724
48725     setSize : function(width, height){
48726         if(!this.ignoreResize(width, height)){
48727             var size = this.adjustForComponents(width, height);
48728             var el = this.layout.getEl();
48729             el.setSize(size.width, size.height);
48730             var touch = el.dom.offsetWidth;
48731             this.layout.layout();
48732             // ie requires a double layout on the first pass
48733             if(Roo.isIE && !this.initialized){
48734                 this.initialized = true;
48735                 this.layout.layout();
48736             }
48737         }
48738     },
48739     
48740     // activate all subpanels if not currently active..
48741     
48742     setActiveState : function(active){
48743         this.active = active;
48744         if(!active){
48745             this.fireEvent("deactivate", this);
48746             return;
48747         }
48748         
48749         this.fireEvent("activate", this);
48750         // not sure if this should happen before or after..
48751         if (!this.layout) {
48752             return; // should not happen..
48753         }
48754         var reg = false;
48755         for (var r in this.layout.regions) {
48756             reg = this.layout.getRegion(r);
48757             if (reg.getActivePanel()) {
48758                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
48759                 reg.setActivePanel(reg.getActivePanel());
48760                 continue;
48761             }
48762             if (!reg.panels.length) {
48763                 continue;
48764             }
48765             reg.showPanel(reg.getPanel(0));
48766         }
48767         
48768         
48769         
48770         
48771     },
48772     
48773     /**
48774      * Returns the nested BorderLayout for this panel
48775      * @return {Roo.BorderLayout} 
48776      */
48777     getLayout : function(){
48778         return this.layout;
48779     },
48780     
48781      /**
48782      * Adds a xtype elements to the layout of the nested panel
48783      * <pre><code>
48784
48785 panel.addxtype({
48786        xtype : 'ContentPanel',
48787        region: 'west',
48788        items: [ .... ]
48789    }
48790 );
48791
48792 panel.addxtype({
48793         xtype : 'NestedLayoutPanel',
48794         region: 'west',
48795         layout: {
48796            center: { },
48797            west: { }   
48798         },
48799         items : [ ... list of content panels or nested layout panels.. ]
48800    }
48801 );
48802 </code></pre>
48803      * @param {Object} cfg Xtype definition of item to add.
48804      */
48805     addxtype : function(cfg) {
48806         return this.layout.addxtype(cfg);
48807     
48808     }
48809 });
48810
48811 Roo.ScrollPanel = function(el, config, content){
48812     config = config || {};
48813     config.fitToFrame = true;
48814     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
48815     
48816     this.el.dom.style.overflow = "hidden";
48817     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
48818     this.el.removeClass("x-layout-inactive-content");
48819     this.el.on("mousewheel", this.onWheel, this);
48820
48821     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
48822     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
48823     up.unselectable(); down.unselectable();
48824     up.on("click", this.scrollUp, this);
48825     down.on("click", this.scrollDown, this);
48826     up.addClassOnOver("x-scroller-btn-over");
48827     down.addClassOnOver("x-scroller-btn-over");
48828     up.addClassOnClick("x-scroller-btn-click");
48829     down.addClassOnClick("x-scroller-btn-click");
48830     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
48831
48832     this.resizeEl = this.el;
48833     this.el = wrap; this.up = up; this.down = down;
48834 };
48835
48836 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
48837     increment : 100,
48838     wheelIncrement : 5,
48839     scrollUp : function(){
48840         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
48841     },
48842
48843     scrollDown : function(){
48844         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
48845     },
48846
48847     afterScroll : function(){
48848         var el = this.resizeEl;
48849         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
48850         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
48851         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
48852     },
48853
48854     setSize : function(){
48855         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
48856         this.afterScroll();
48857     },
48858
48859     onWheel : function(e){
48860         var d = e.getWheelDelta();
48861         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
48862         this.afterScroll();
48863         e.stopEvent();
48864     },
48865
48866     setContent : function(content, loadScripts){
48867         this.resizeEl.update(content, loadScripts);
48868     }
48869
48870 });
48871
48872
48873
48874
48875
48876
48877
48878
48879
48880 /**
48881  * @class Roo.TreePanel
48882  * @extends Roo.ContentPanel
48883  * @constructor
48884  * Create a new TreePanel. - defaults to fit/scoll contents.
48885  * @param {String/Object} config A string to set only the panel's title, or a config object
48886  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
48887  */
48888 Roo.TreePanel = function(config){
48889     var el = config.el;
48890     var tree = config.tree;
48891     delete config.tree; 
48892     delete config.el; // hopefull!
48893     
48894     // wrapper for IE7 strict & safari scroll issue
48895     
48896     var treeEl = el.createChild();
48897     config.resizeEl = treeEl;
48898     
48899     
48900     
48901     Roo.TreePanel.superclass.constructor.call(this, el, config);
48902  
48903  
48904     this.tree = new Roo.tree.TreePanel(treeEl , tree);
48905     //console.log(tree);
48906     this.on('activate', function()
48907     {
48908         if (this.tree.rendered) {
48909             return;
48910         }
48911         //console.log('render tree');
48912         this.tree.render();
48913     });
48914     // this should not be needed.. - it's actually the 'el' that resizes?
48915     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
48916     
48917     //this.on('resize',  function (cp, w, h) {
48918     //        this.tree.innerCt.setWidth(w);
48919     //        this.tree.innerCt.setHeight(h);
48920     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
48921     //});
48922
48923         
48924     
48925 };
48926
48927 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
48928     fitToFrame : true,
48929     autoScroll : true
48930 });
48931
48932
48933
48934
48935
48936
48937
48938
48939
48940
48941
48942 /*
48943  * Based on:
48944  * Ext JS Library 1.1.1
48945  * Copyright(c) 2006-2007, Ext JS, LLC.
48946  *
48947  * Originally Released Under LGPL - original licence link has changed is not relivant.
48948  *
48949  * Fork - LGPL
48950  * <script type="text/javascript">
48951  */
48952  
48953
48954 /**
48955  * @class Roo.ReaderLayout
48956  * @extends Roo.BorderLayout
48957  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
48958  * center region containing two nested regions (a top one for a list view and one for item preview below),
48959  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
48960  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
48961  * expedites the setup of the overall layout and regions for this common application style.
48962  * Example:
48963  <pre><code>
48964 var reader = new Roo.ReaderLayout();
48965 var CP = Roo.ContentPanel;  // shortcut for adding
48966
48967 reader.beginUpdate();
48968 reader.add("north", new CP("north", "North"));
48969 reader.add("west", new CP("west", {title: "West"}));
48970 reader.add("east", new CP("east", {title: "East"}));
48971
48972 reader.regions.listView.add(new CP("listView", "List"));
48973 reader.regions.preview.add(new CP("preview", "Preview"));
48974 reader.endUpdate();
48975 </code></pre>
48976 * @constructor
48977 * Create a new ReaderLayout
48978 * @param {Object} config Configuration options
48979 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
48980 * document.body if omitted)
48981 */
48982 Roo.ReaderLayout = function(config, renderTo){
48983     var c = config || {size:{}};
48984     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
48985         north: c.north !== false ? Roo.apply({
48986             split:false,
48987             initialSize: 32,
48988             titlebar: false
48989         }, c.north) : false,
48990         west: c.west !== false ? Roo.apply({
48991             split:true,
48992             initialSize: 200,
48993             minSize: 175,
48994             maxSize: 400,
48995             titlebar: true,
48996             collapsible: true,
48997             animate: true,
48998             margins:{left:5,right:0,bottom:5,top:5},
48999             cmargins:{left:5,right:5,bottom:5,top:5}
49000         }, c.west) : false,
49001         east: c.east !== false ? Roo.apply({
49002             split:true,
49003             initialSize: 200,
49004             minSize: 175,
49005             maxSize: 400,
49006             titlebar: true,
49007             collapsible: true,
49008             animate: true,
49009             margins:{left:0,right:5,bottom:5,top:5},
49010             cmargins:{left:5,right:5,bottom:5,top:5}
49011         }, c.east) : false,
49012         center: Roo.apply({
49013             tabPosition: 'top',
49014             autoScroll:false,
49015             closeOnTab: true,
49016             titlebar:false,
49017             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
49018         }, c.center)
49019     });
49020
49021     this.el.addClass('x-reader');
49022
49023     this.beginUpdate();
49024
49025     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
49026         south: c.preview !== false ? Roo.apply({
49027             split:true,
49028             initialSize: 200,
49029             minSize: 100,
49030             autoScroll:true,
49031             collapsible:true,
49032             titlebar: true,
49033             cmargins:{top:5,left:0, right:0, bottom:0}
49034         }, c.preview) : false,
49035         center: Roo.apply({
49036             autoScroll:false,
49037             titlebar:false,
49038             minHeight:200
49039         }, c.listView)
49040     });
49041     this.add('center', new Roo.NestedLayoutPanel(inner,
49042             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
49043
49044     this.endUpdate();
49045
49046     this.regions.preview = inner.getRegion('south');
49047     this.regions.listView = inner.getRegion('center');
49048 };
49049
49050 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
49051  * Based on:
49052  * Ext JS Library 1.1.1
49053  * Copyright(c) 2006-2007, Ext JS, LLC.
49054  *
49055  * Originally Released Under LGPL - original licence link has changed is not relivant.
49056  *
49057  * Fork - LGPL
49058  * <script type="text/javascript">
49059  */
49060  
49061 /**
49062  * @class Roo.grid.Grid
49063  * @extends Roo.util.Observable
49064  * This class represents the primary interface of a component based grid control.
49065  * <br><br>Usage:<pre><code>
49066  var grid = new Roo.grid.Grid("my-container-id", {
49067      ds: myDataStore,
49068      cm: myColModel,
49069      selModel: mySelectionModel,
49070      autoSizeColumns: true,
49071      monitorWindowResize: false,
49072      trackMouseOver: true
49073  });
49074  // set any options
49075  grid.render();
49076  * </code></pre>
49077  * <b>Common Problems:</b><br/>
49078  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
49079  * element will correct this<br/>
49080  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
49081  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
49082  * are unpredictable.<br/>
49083  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
49084  * grid to calculate dimensions/offsets.<br/>
49085   * @constructor
49086  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
49087  * The container MUST have some type of size defined for the grid to fill. The container will be
49088  * automatically set to position relative if it isn't already.
49089  * @param {Object} config A config object that sets properties on this grid.
49090  */
49091 Roo.grid.Grid = function(container, config){
49092         // initialize the container
49093         this.container = Roo.get(container);
49094         this.container.update("");
49095         this.container.setStyle("overflow", "hidden");
49096     this.container.addClass('x-grid-container');
49097
49098     this.id = this.container.id;
49099
49100     Roo.apply(this, config);
49101     // check and correct shorthanded configs
49102     if(this.ds){
49103         this.dataSource = this.ds;
49104         delete this.ds;
49105     }
49106     if(this.cm){
49107         this.colModel = this.cm;
49108         delete this.cm;
49109     }
49110     if(this.sm){
49111         this.selModel = this.sm;
49112         delete this.sm;
49113     }
49114
49115     if (this.selModel) {
49116         this.selModel = Roo.factory(this.selModel, Roo.grid);
49117         this.sm = this.selModel;
49118         this.sm.xmodule = this.xmodule || false;
49119     }
49120     if (typeof(this.colModel.config) == 'undefined') {
49121         this.colModel = new Roo.grid.ColumnModel(this.colModel);
49122         this.cm = this.colModel;
49123         this.cm.xmodule = this.xmodule || false;
49124     }
49125     if (this.dataSource) {
49126         this.dataSource= Roo.factory(this.dataSource, Roo.data);
49127         this.ds = this.dataSource;
49128         this.ds.xmodule = this.xmodule || false;
49129          
49130     }
49131     
49132     
49133     
49134     if(this.width){
49135         this.container.setWidth(this.width);
49136     }
49137
49138     if(this.height){
49139         this.container.setHeight(this.height);
49140     }
49141     /** @private */
49142         this.addEvents({
49143         // raw events
49144         /**
49145          * @event click
49146          * The raw click event for the entire grid.
49147          * @param {Roo.EventObject} e
49148          */
49149         "click" : true,
49150         /**
49151          * @event dblclick
49152          * The raw dblclick event for the entire grid.
49153          * @param {Roo.EventObject} e
49154          */
49155         "dblclick" : true,
49156         /**
49157          * @event contextmenu
49158          * The raw contextmenu event for the entire grid.
49159          * @param {Roo.EventObject} e
49160          */
49161         "contextmenu" : true,
49162         /**
49163          * @event mousedown
49164          * The raw mousedown event for the entire grid.
49165          * @param {Roo.EventObject} e
49166          */
49167         "mousedown" : true,
49168         /**
49169          * @event mouseup
49170          * The raw mouseup event for the entire grid.
49171          * @param {Roo.EventObject} e
49172          */
49173         "mouseup" : true,
49174         /**
49175          * @event mouseover
49176          * The raw mouseover event for the entire grid.
49177          * @param {Roo.EventObject} e
49178          */
49179         "mouseover" : true,
49180         /**
49181          * @event mouseout
49182          * The raw mouseout event for the entire grid.
49183          * @param {Roo.EventObject} e
49184          */
49185         "mouseout" : true,
49186         /**
49187          * @event keypress
49188          * The raw keypress event for the entire grid.
49189          * @param {Roo.EventObject} e
49190          */
49191         "keypress" : true,
49192         /**
49193          * @event keydown
49194          * The raw keydown event for the entire grid.
49195          * @param {Roo.EventObject} e
49196          */
49197         "keydown" : true,
49198
49199         // custom events
49200
49201         /**
49202          * @event cellclick
49203          * Fires when a cell is clicked
49204          * @param {Grid} this
49205          * @param {Number} rowIndex
49206          * @param {Number} columnIndex
49207          * @param {Roo.EventObject} e
49208          */
49209         "cellclick" : true,
49210         /**
49211          * @event celldblclick
49212          * Fires when a cell is double clicked
49213          * @param {Grid} this
49214          * @param {Number} rowIndex
49215          * @param {Number} columnIndex
49216          * @param {Roo.EventObject} e
49217          */
49218         "celldblclick" : true,
49219         /**
49220          * @event rowclick
49221          * Fires when a row is clicked
49222          * @param {Grid} this
49223          * @param {Number} rowIndex
49224          * @param {Roo.EventObject} e
49225          */
49226         "rowclick" : true,
49227         /**
49228          * @event rowdblclick
49229          * Fires when a row is double clicked
49230          * @param {Grid} this
49231          * @param {Number} rowIndex
49232          * @param {Roo.EventObject} e
49233          */
49234         "rowdblclick" : true,
49235         /**
49236          * @event headerclick
49237          * Fires when a header is clicked
49238          * @param {Grid} this
49239          * @param {Number} columnIndex
49240          * @param {Roo.EventObject} e
49241          */
49242         "headerclick" : true,
49243         /**
49244          * @event headerdblclick
49245          * Fires when a header cell is double clicked
49246          * @param {Grid} this
49247          * @param {Number} columnIndex
49248          * @param {Roo.EventObject} e
49249          */
49250         "headerdblclick" : true,
49251         /**
49252          * @event rowcontextmenu
49253          * Fires when a row is right clicked
49254          * @param {Grid} this
49255          * @param {Number} rowIndex
49256          * @param {Roo.EventObject} e
49257          */
49258         "rowcontextmenu" : true,
49259         /**
49260          * @event cellcontextmenu
49261          * Fires when a cell is right clicked
49262          * @param {Grid} this
49263          * @param {Number} rowIndex
49264          * @param {Number} cellIndex
49265          * @param {Roo.EventObject} e
49266          */
49267          "cellcontextmenu" : true,
49268         /**
49269          * @event headercontextmenu
49270          * Fires when a header is right clicked
49271          * @param {Grid} this
49272          * @param {Number} columnIndex
49273          * @param {Roo.EventObject} e
49274          */
49275         "headercontextmenu" : true,
49276         /**
49277          * @event bodyscroll
49278          * Fires when the body element is scrolled
49279          * @param {Number} scrollLeft
49280          * @param {Number} scrollTop
49281          */
49282         "bodyscroll" : true,
49283         /**
49284          * @event columnresize
49285          * Fires when the user resizes a column
49286          * @param {Number} columnIndex
49287          * @param {Number} newSize
49288          */
49289         "columnresize" : true,
49290         /**
49291          * @event columnmove
49292          * Fires when the user moves a column
49293          * @param {Number} oldIndex
49294          * @param {Number} newIndex
49295          */
49296         "columnmove" : true,
49297         /**
49298          * @event startdrag
49299          * Fires when row(s) start being dragged
49300          * @param {Grid} this
49301          * @param {Roo.GridDD} dd The drag drop object
49302          * @param {event} e The raw browser event
49303          */
49304         "startdrag" : true,
49305         /**
49306          * @event enddrag
49307          * Fires when a drag operation is complete
49308          * @param {Grid} this
49309          * @param {Roo.GridDD} dd The drag drop object
49310          * @param {event} e The raw browser event
49311          */
49312         "enddrag" : true,
49313         /**
49314          * @event dragdrop
49315          * Fires when dragged row(s) are dropped on a valid DD target
49316          * @param {Grid} this
49317          * @param {Roo.GridDD} dd The drag drop object
49318          * @param {String} targetId The target drag drop object
49319          * @param {event} e The raw browser event
49320          */
49321         "dragdrop" : true,
49322         /**
49323          * @event dragover
49324          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
49325          * @param {Grid} this
49326          * @param {Roo.GridDD} dd The drag drop object
49327          * @param {String} targetId The target drag drop object
49328          * @param {event} e The raw browser event
49329          */
49330         "dragover" : true,
49331         /**
49332          * @event dragenter
49333          *  Fires when the dragged row(s) first cross another DD target while being dragged
49334          * @param {Grid} this
49335          * @param {Roo.GridDD} dd The drag drop object
49336          * @param {String} targetId The target drag drop object
49337          * @param {event} e The raw browser event
49338          */
49339         "dragenter" : true,
49340         /**
49341          * @event dragout
49342          * Fires when the dragged row(s) leave another DD target while being dragged
49343          * @param {Grid} this
49344          * @param {Roo.GridDD} dd The drag drop object
49345          * @param {String} targetId The target drag drop object
49346          * @param {event} e The raw browser event
49347          */
49348         "dragout" : true,
49349         /**
49350          * @event rowclass
49351          * Fires when a row is rendered, so you can change add a style to it.
49352          * @param {GridView} gridview   The grid view
49353          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
49354          */
49355         'rowclass' : true,
49356
49357         /**
49358          * @event render
49359          * Fires when the grid is rendered
49360          * @param {Grid} grid
49361          */
49362         'render' : true
49363     });
49364
49365     Roo.grid.Grid.superclass.constructor.call(this);
49366 };
49367 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
49368     
49369     /**
49370      * @cfg {String} ddGroup - drag drop group.
49371      */
49372
49373     /**
49374      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
49375      */
49376     minColumnWidth : 25,
49377
49378     /**
49379      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
49380      * <b>on initial render.</b> It is more efficient to explicitly size the columns
49381      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
49382      */
49383     autoSizeColumns : false,
49384
49385     /**
49386      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
49387      */
49388     autoSizeHeaders : true,
49389
49390     /**
49391      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
49392      */
49393     monitorWindowResize : true,
49394
49395     /**
49396      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
49397      * rows measured to get a columns size. Default is 0 (all rows).
49398      */
49399     maxRowsToMeasure : 0,
49400
49401     /**
49402      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
49403      */
49404     trackMouseOver : true,
49405
49406     /**
49407     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
49408     */
49409     
49410     /**
49411     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
49412     */
49413     enableDragDrop : false,
49414     
49415     /**
49416     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
49417     */
49418     enableColumnMove : true,
49419     
49420     /**
49421     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
49422     */
49423     enableColumnHide : true,
49424     
49425     /**
49426     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
49427     */
49428     enableRowHeightSync : false,
49429     
49430     /**
49431     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
49432     */
49433     stripeRows : true,
49434     
49435     /**
49436     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
49437     */
49438     autoHeight : false,
49439
49440     /**
49441      * @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.
49442      */
49443     autoExpandColumn : false,
49444
49445     /**
49446     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
49447     * Default is 50.
49448     */
49449     autoExpandMin : 50,
49450
49451     /**
49452     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
49453     */
49454     autoExpandMax : 1000,
49455
49456     /**
49457     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
49458     */
49459     view : null,
49460
49461     /**
49462     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
49463     */
49464     loadMask : false,
49465     /**
49466     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
49467     */
49468     dropTarget: false,
49469     
49470    
49471     
49472     // private
49473     rendered : false,
49474
49475     /**
49476     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
49477     * of a fixed width. Default is false.
49478     */
49479     /**
49480     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
49481     */
49482     /**
49483      * Called once after all setup has been completed and the grid is ready to be rendered.
49484      * @return {Roo.grid.Grid} this
49485      */
49486     render : function()
49487     {
49488         var c = this.container;
49489         // try to detect autoHeight/width mode
49490         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
49491             this.autoHeight = true;
49492         }
49493         var view = this.getView();
49494         view.init(this);
49495
49496         c.on("click", this.onClick, this);
49497         c.on("dblclick", this.onDblClick, this);
49498         c.on("contextmenu", this.onContextMenu, this);
49499         c.on("keydown", this.onKeyDown, this);
49500
49501         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
49502
49503         this.getSelectionModel().init(this);
49504
49505         view.render();
49506
49507         if(this.loadMask){
49508             this.loadMask = new Roo.LoadMask(this.container,
49509                     Roo.apply({store:this.dataSource}, this.loadMask));
49510         }
49511         
49512         
49513         if (this.toolbar && this.toolbar.xtype) {
49514             this.toolbar.container = this.getView().getHeaderPanel(true);
49515             this.toolbar = new Roo.Toolbar(this.toolbar);
49516         }
49517         if (this.footer && this.footer.xtype) {
49518             this.footer.dataSource = this.getDataSource();
49519             this.footer.container = this.getView().getFooterPanel(true);
49520             this.footer = Roo.factory(this.footer, Roo);
49521         }
49522         if (this.dropTarget && this.dropTarget.xtype) {
49523             delete this.dropTarget.xtype;
49524             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
49525         }
49526         
49527         
49528         this.rendered = true;
49529         this.fireEvent('render', this);
49530         return this;
49531     },
49532
49533         /**
49534          * Reconfigures the grid to use a different Store and Column Model.
49535          * The View will be bound to the new objects and refreshed.
49536          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
49537          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
49538          */
49539     reconfigure : function(dataSource, colModel){
49540         if(this.loadMask){
49541             this.loadMask.destroy();
49542             this.loadMask = new Roo.LoadMask(this.container,
49543                     Roo.apply({store:dataSource}, this.loadMask));
49544         }
49545         this.view.bind(dataSource, colModel);
49546         this.dataSource = dataSource;
49547         this.colModel = colModel;
49548         this.view.refresh(true);
49549     },
49550
49551     // private
49552     onKeyDown : function(e){
49553         this.fireEvent("keydown", e);
49554     },
49555
49556     /**
49557      * Destroy this grid.
49558      * @param {Boolean} removeEl True to remove the element
49559      */
49560     destroy : function(removeEl, keepListeners){
49561         if(this.loadMask){
49562             this.loadMask.destroy();
49563         }
49564         var c = this.container;
49565         c.removeAllListeners();
49566         this.view.destroy();
49567         this.colModel.purgeListeners();
49568         if(!keepListeners){
49569             this.purgeListeners();
49570         }
49571         c.update("");
49572         if(removeEl === true){
49573             c.remove();
49574         }
49575     },
49576
49577     // private
49578     processEvent : function(name, e){
49579         this.fireEvent(name, e);
49580         var t = e.getTarget();
49581         var v = this.view;
49582         var header = v.findHeaderIndex(t);
49583         if(header !== false){
49584             this.fireEvent("header" + name, this, header, e);
49585         }else{
49586             var row = v.findRowIndex(t);
49587             var cell = v.findCellIndex(t);
49588             if(row !== false){
49589                 this.fireEvent("row" + name, this, row, e);
49590                 if(cell !== false){
49591                     this.fireEvent("cell" + name, this, row, cell, e);
49592                 }
49593             }
49594         }
49595     },
49596
49597     // private
49598     onClick : function(e){
49599         this.processEvent("click", e);
49600     },
49601
49602     // private
49603     onContextMenu : function(e, t){
49604         this.processEvent("contextmenu", e);
49605     },
49606
49607     // private
49608     onDblClick : function(e){
49609         this.processEvent("dblclick", e);
49610     },
49611
49612     // private
49613     walkCells : function(row, col, step, fn, scope){
49614         var cm = this.colModel, clen = cm.getColumnCount();
49615         var ds = this.dataSource, rlen = ds.getCount(), first = true;
49616         if(step < 0){
49617             if(col < 0){
49618                 row--;
49619                 first = false;
49620             }
49621             while(row >= 0){
49622                 if(!first){
49623                     col = clen-1;
49624                 }
49625                 first = false;
49626                 while(col >= 0){
49627                     if(fn.call(scope || this, row, col, cm) === true){
49628                         return [row, col];
49629                     }
49630                     col--;
49631                 }
49632                 row--;
49633             }
49634         } else {
49635             if(col >= clen){
49636                 row++;
49637                 first = false;
49638             }
49639             while(row < rlen){
49640                 if(!first){
49641                     col = 0;
49642                 }
49643                 first = false;
49644                 while(col < clen){
49645                     if(fn.call(scope || this, row, col, cm) === true){
49646                         return [row, col];
49647                     }
49648                     col++;
49649                 }
49650                 row++;
49651             }
49652         }
49653         return null;
49654     },
49655
49656     // private
49657     getSelections : function(){
49658         return this.selModel.getSelections();
49659     },
49660
49661     /**
49662      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
49663      * but if manual update is required this method will initiate it.
49664      */
49665     autoSize : function(){
49666         if(this.rendered){
49667             this.view.layout();
49668             if(this.view.adjustForScroll){
49669                 this.view.adjustForScroll();
49670             }
49671         }
49672     },
49673
49674     /**
49675      * Returns the grid's underlying element.
49676      * @return {Element} The element
49677      */
49678     getGridEl : function(){
49679         return this.container;
49680     },
49681
49682     // private for compatibility, overridden by editor grid
49683     stopEditing : function(){},
49684
49685     /**
49686      * Returns the grid's SelectionModel.
49687      * @return {SelectionModel}
49688      */
49689     getSelectionModel : function(){
49690         if(!this.selModel){
49691             this.selModel = new Roo.grid.RowSelectionModel();
49692         }
49693         return this.selModel;
49694     },
49695
49696     /**
49697      * Returns the grid's DataSource.
49698      * @return {DataSource}
49699      */
49700     getDataSource : function(){
49701         return this.dataSource;
49702     },
49703
49704     /**
49705      * Returns the grid's ColumnModel.
49706      * @return {ColumnModel}
49707      */
49708     getColumnModel : function(){
49709         return this.colModel;
49710     },
49711
49712     /**
49713      * Returns the grid's GridView object.
49714      * @return {GridView}
49715      */
49716     getView : function(){
49717         if(!this.view){
49718             this.view = new Roo.grid.GridView(this.viewConfig);
49719         }
49720         return this.view;
49721     },
49722     /**
49723      * Called to get grid's drag proxy text, by default returns this.ddText.
49724      * @return {String}
49725      */
49726     getDragDropText : function(){
49727         var count = this.selModel.getCount();
49728         return String.format(this.ddText, count, count == 1 ? '' : 's');
49729     }
49730 });
49731 /**
49732  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
49733  * %0 is replaced with the number of selected rows.
49734  * @type String
49735  */
49736 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
49737  * Based on:
49738  * Ext JS Library 1.1.1
49739  * Copyright(c) 2006-2007, Ext JS, LLC.
49740  *
49741  * Originally Released Under LGPL - original licence link has changed is not relivant.
49742  *
49743  * Fork - LGPL
49744  * <script type="text/javascript">
49745  */
49746  
49747 Roo.grid.AbstractGridView = function(){
49748         this.grid = null;
49749         
49750         this.events = {
49751             "beforerowremoved" : true,
49752             "beforerowsinserted" : true,
49753             "beforerefresh" : true,
49754             "rowremoved" : true,
49755             "rowsinserted" : true,
49756             "rowupdated" : true,
49757             "refresh" : true
49758         };
49759     Roo.grid.AbstractGridView.superclass.constructor.call(this);
49760 };
49761
49762 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
49763     rowClass : "x-grid-row",
49764     cellClass : "x-grid-cell",
49765     tdClass : "x-grid-td",
49766     hdClass : "x-grid-hd",
49767     splitClass : "x-grid-hd-split",
49768     
49769         init: function(grid){
49770         this.grid = grid;
49771                 var cid = this.grid.getGridEl().id;
49772         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
49773         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
49774         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
49775         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
49776         },
49777         
49778         getColumnRenderers : function(){
49779         var renderers = [];
49780         var cm = this.grid.colModel;
49781         var colCount = cm.getColumnCount();
49782         for(var i = 0; i < colCount; i++){
49783             renderers[i] = cm.getRenderer(i);
49784         }
49785         return renderers;
49786     },
49787     
49788     getColumnIds : function(){
49789         var ids = [];
49790         var cm = this.grid.colModel;
49791         var colCount = cm.getColumnCount();
49792         for(var i = 0; i < colCount; i++){
49793             ids[i] = cm.getColumnId(i);
49794         }
49795         return ids;
49796     },
49797     
49798     getDataIndexes : function(){
49799         if(!this.indexMap){
49800             this.indexMap = this.buildIndexMap();
49801         }
49802         return this.indexMap.colToData;
49803     },
49804     
49805     getColumnIndexByDataIndex : function(dataIndex){
49806         if(!this.indexMap){
49807             this.indexMap = this.buildIndexMap();
49808         }
49809         return this.indexMap.dataToCol[dataIndex];
49810     },
49811     
49812     /**
49813      * Set a css style for a column dynamically. 
49814      * @param {Number} colIndex The index of the column
49815      * @param {String} name The css property name
49816      * @param {String} value The css value
49817      */
49818     setCSSStyle : function(colIndex, name, value){
49819         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
49820         Roo.util.CSS.updateRule(selector, name, value);
49821     },
49822     
49823     generateRules : function(cm){
49824         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
49825         Roo.util.CSS.removeStyleSheet(rulesId);
49826         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49827             var cid = cm.getColumnId(i);
49828             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
49829                          this.tdSelector, cid, " {\n}\n",
49830                          this.hdSelector, cid, " {\n}\n",
49831                          this.splitSelector, cid, " {\n}\n");
49832         }
49833         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49834     }
49835 });/*
49836  * Based on:
49837  * Ext JS Library 1.1.1
49838  * Copyright(c) 2006-2007, Ext JS, LLC.
49839  *
49840  * Originally Released Under LGPL - original licence link has changed is not relivant.
49841  *
49842  * Fork - LGPL
49843  * <script type="text/javascript">
49844  */
49845
49846 // private
49847 // This is a support class used internally by the Grid components
49848 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
49849     this.grid = grid;
49850     this.view = grid.getView();
49851     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
49852     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
49853     if(hd2){
49854         this.setHandleElId(Roo.id(hd));
49855         this.setOuterHandleElId(Roo.id(hd2));
49856     }
49857     this.scroll = false;
49858 };
49859 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
49860     maxDragWidth: 120,
49861     getDragData : function(e){
49862         var t = Roo.lib.Event.getTarget(e);
49863         var h = this.view.findHeaderCell(t);
49864         if(h){
49865             return {ddel: h.firstChild, header:h};
49866         }
49867         return false;
49868     },
49869
49870     onInitDrag : function(e){
49871         this.view.headersDisabled = true;
49872         var clone = this.dragData.ddel.cloneNode(true);
49873         clone.id = Roo.id();
49874         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
49875         this.proxy.update(clone);
49876         return true;
49877     },
49878
49879     afterValidDrop : function(){
49880         var v = this.view;
49881         setTimeout(function(){
49882             v.headersDisabled = false;
49883         }, 50);
49884     },
49885
49886     afterInvalidDrop : function(){
49887         var v = this.view;
49888         setTimeout(function(){
49889             v.headersDisabled = false;
49890         }, 50);
49891     }
49892 });
49893 /*
49894  * Based on:
49895  * Ext JS Library 1.1.1
49896  * Copyright(c) 2006-2007, Ext JS, LLC.
49897  *
49898  * Originally Released Under LGPL - original licence link has changed is not relivant.
49899  *
49900  * Fork - LGPL
49901  * <script type="text/javascript">
49902  */
49903 // private
49904 // This is a support class used internally by the Grid components
49905 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
49906     this.grid = grid;
49907     this.view = grid.getView();
49908     // split the proxies so they don't interfere with mouse events
49909     this.proxyTop = Roo.DomHelper.append(document.body, {
49910         cls:"col-move-top", html:"&#160;"
49911     }, true);
49912     this.proxyBottom = Roo.DomHelper.append(document.body, {
49913         cls:"col-move-bottom", html:"&#160;"
49914     }, true);
49915     this.proxyTop.hide = this.proxyBottom.hide = function(){
49916         this.setLeftTop(-100,-100);
49917         this.setStyle("visibility", "hidden");
49918     };
49919     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
49920     // temporarily disabled
49921     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
49922     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
49923 };
49924 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
49925     proxyOffsets : [-4, -9],
49926     fly: Roo.Element.fly,
49927
49928     getTargetFromEvent : function(e){
49929         var t = Roo.lib.Event.getTarget(e);
49930         var cindex = this.view.findCellIndex(t);
49931         if(cindex !== false){
49932             return this.view.getHeaderCell(cindex);
49933         }
49934         return null;
49935     },
49936
49937     nextVisible : function(h){
49938         var v = this.view, cm = this.grid.colModel;
49939         h = h.nextSibling;
49940         while(h){
49941             if(!cm.isHidden(v.getCellIndex(h))){
49942                 return h;
49943             }
49944             h = h.nextSibling;
49945         }
49946         return null;
49947     },
49948
49949     prevVisible : function(h){
49950         var v = this.view, cm = this.grid.colModel;
49951         h = h.prevSibling;
49952         while(h){
49953             if(!cm.isHidden(v.getCellIndex(h))){
49954                 return h;
49955             }
49956             h = h.prevSibling;
49957         }
49958         return null;
49959     },
49960
49961     positionIndicator : function(h, n, e){
49962         var x = Roo.lib.Event.getPageX(e);
49963         var r = Roo.lib.Dom.getRegion(n.firstChild);
49964         var px, pt, py = r.top + this.proxyOffsets[1];
49965         if((r.right - x) <= (r.right-r.left)/2){
49966             px = r.right+this.view.borderWidth;
49967             pt = "after";
49968         }else{
49969             px = r.left;
49970             pt = "before";
49971         }
49972         var oldIndex = this.view.getCellIndex(h);
49973         var newIndex = this.view.getCellIndex(n);
49974
49975         if(this.grid.colModel.isFixed(newIndex)){
49976             return false;
49977         }
49978
49979         var locked = this.grid.colModel.isLocked(newIndex);
49980
49981         if(pt == "after"){
49982             newIndex++;
49983         }
49984         if(oldIndex < newIndex){
49985             newIndex--;
49986         }
49987         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
49988             return false;
49989         }
49990         px +=  this.proxyOffsets[0];
49991         this.proxyTop.setLeftTop(px, py);
49992         this.proxyTop.show();
49993         if(!this.bottomOffset){
49994             this.bottomOffset = this.view.mainHd.getHeight();
49995         }
49996         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
49997         this.proxyBottom.show();
49998         return pt;
49999     },
50000
50001     onNodeEnter : function(n, dd, e, data){
50002         if(data.header != n){
50003             this.positionIndicator(data.header, n, e);
50004         }
50005     },
50006
50007     onNodeOver : function(n, dd, e, data){
50008         var result = false;
50009         if(data.header != n){
50010             result = this.positionIndicator(data.header, n, e);
50011         }
50012         if(!result){
50013             this.proxyTop.hide();
50014             this.proxyBottom.hide();
50015         }
50016         return result ? this.dropAllowed : this.dropNotAllowed;
50017     },
50018
50019     onNodeOut : function(n, dd, e, data){
50020         this.proxyTop.hide();
50021         this.proxyBottom.hide();
50022     },
50023
50024     onNodeDrop : function(n, dd, e, data){
50025         var h = data.header;
50026         if(h != n){
50027             var cm = this.grid.colModel;
50028             var x = Roo.lib.Event.getPageX(e);
50029             var r = Roo.lib.Dom.getRegion(n.firstChild);
50030             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
50031             var oldIndex = this.view.getCellIndex(h);
50032             var newIndex = this.view.getCellIndex(n);
50033             var locked = cm.isLocked(newIndex);
50034             if(pt == "after"){
50035                 newIndex++;
50036             }
50037             if(oldIndex < newIndex){
50038                 newIndex--;
50039             }
50040             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
50041                 return false;
50042             }
50043             cm.setLocked(oldIndex, locked, true);
50044             cm.moveColumn(oldIndex, newIndex);
50045             this.grid.fireEvent("columnmove", oldIndex, newIndex);
50046             return true;
50047         }
50048         return false;
50049     }
50050 });
50051 /*
50052  * Based on:
50053  * Ext JS Library 1.1.1
50054  * Copyright(c) 2006-2007, Ext JS, LLC.
50055  *
50056  * Originally Released Under LGPL - original licence link has changed is not relivant.
50057  *
50058  * Fork - LGPL
50059  * <script type="text/javascript">
50060  */
50061   
50062 /**
50063  * @class Roo.grid.GridView
50064  * @extends Roo.util.Observable
50065  *
50066  * @constructor
50067  * @param {Object} config
50068  */
50069 Roo.grid.GridView = function(config){
50070     Roo.grid.GridView.superclass.constructor.call(this);
50071     this.el = null;
50072
50073     Roo.apply(this, config);
50074 };
50075
50076 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
50077
50078     unselectable :  'unselectable="on"',
50079     unselectableCls :  'x-unselectable',
50080     
50081     
50082     rowClass : "x-grid-row",
50083
50084     cellClass : "x-grid-col",
50085
50086     tdClass : "x-grid-td",
50087
50088     hdClass : "x-grid-hd",
50089
50090     splitClass : "x-grid-split",
50091
50092     sortClasses : ["sort-asc", "sort-desc"],
50093
50094     enableMoveAnim : false,
50095
50096     hlColor: "C3DAF9",
50097
50098     dh : Roo.DomHelper,
50099
50100     fly : Roo.Element.fly,
50101
50102     css : Roo.util.CSS,
50103
50104     borderWidth: 1,
50105
50106     splitOffset: 3,
50107
50108     scrollIncrement : 22,
50109
50110     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
50111
50112     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
50113
50114     bind : function(ds, cm){
50115         if(this.ds){
50116             this.ds.un("load", this.onLoad, this);
50117             this.ds.un("datachanged", this.onDataChange, this);
50118             this.ds.un("add", this.onAdd, this);
50119             this.ds.un("remove", this.onRemove, this);
50120             this.ds.un("update", this.onUpdate, this);
50121             this.ds.un("clear", this.onClear, this);
50122         }
50123         if(ds){
50124             ds.on("load", this.onLoad, this);
50125             ds.on("datachanged", this.onDataChange, this);
50126             ds.on("add", this.onAdd, this);
50127             ds.on("remove", this.onRemove, this);
50128             ds.on("update", this.onUpdate, this);
50129             ds.on("clear", this.onClear, this);
50130         }
50131         this.ds = ds;
50132
50133         if(this.cm){
50134             this.cm.un("widthchange", this.onColWidthChange, this);
50135             this.cm.un("headerchange", this.onHeaderChange, this);
50136             this.cm.un("hiddenchange", this.onHiddenChange, this);
50137             this.cm.un("columnmoved", this.onColumnMove, this);
50138             this.cm.un("columnlockchange", this.onColumnLock, this);
50139         }
50140         if(cm){
50141             this.generateRules(cm);
50142             cm.on("widthchange", this.onColWidthChange, this);
50143             cm.on("headerchange", this.onHeaderChange, this);
50144             cm.on("hiddenchange", this.onHiddenChange, this);
50145             cm.on("columnmoved", this.onColumnMove, this);
50146             cm.on("columnlockchange", this.onColumnLock, this);
50147         }
50148         this.cm = cm;
50149     },
50150
50151     init: function(grid){
50152         Roo.grid.GridView.superclass.init.call(this, grid);
50153
50154         this.bind(grid.dataSource, grid.colModel);
50155
50156         grid.on("headerclick", this.handleHeaderClick, this);
50157
50158         if(grid.trackMouseOver){
50159             grid.on("mouseover", this.onRowOver, this);
50160             grid.on("mouseout", this.onRowOut, this);
50161         }
50162         grid.cancelTextSelection = function(){};
50163         this.gridId = grid.id;
50164
50165         var tpls = this.templates || {};
50166
50167         if(!tpls.master){
50168             tpls.master = new Roo.Template(
50169                '<div class="x-grid" hidefocus="true">',
50170                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
50171                   '<div class="x-grid-topbar"></div>',
50172                   '<div class="x-grid-scroller"><div></div></div>',
50173                   '<div class="x-grid-locked">',
50174                       '<div class="x-grid-header">{lockedHeader}</div>',
50175                       '<div class="x-grid-body">{lockedBody}</div>',
50176                   "</div>",
50177                   '<div class="x-grid-viewport">',
50178                       '<div class="x-grid-header">{header}</div>',
50179                       '<div class="x-grid-body">{body}</div>',
50180                   "</div>",
50181                   '<div class="x-grid-bottombar"></div>',
50182                  
50183                   '<div class="x-grid-resize-proxy">&#160;</div>',
50184                "</div>"
50185             );
50186             tpls.master.disableformats = true;
50187         }
50188
50189         if(!tpls.header){
50190             tpls.header = new Roo.Template(
50191                '<table border="0" cellspacing="0" cellpadding="0">',
50192                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
50193                "</table>{splits}"
50194             );
50195             tpls.header.disableformats = true;
50196         }
50197         tpls.header.compile();
50198
50199         if(!tpls.hcell){
50200             tpls.hcell = new Roo.Template(
50201                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
50202                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
50203                 "</div></td>"
50204              );
50205              tpls.hcell.disableFormats = true;
50206         }
50207         tpls.hcell.compile();
50208
50209         if(!tpls.hsplit){
50210             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
50211                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
50212             tpls.hsplit.disableFormats = true;
50213         }
50214         tpls.hsplit.compile();
50215
50216         if(!tpls.body){
50217             tpls.body = new Roo.Template(
50218                '<table border="0" cellspacing="0" cellpadding="0">',
50219                "<tbody>{rows}</tbody>",
50220                "</table>"
50221             );
50222             tpls.body.disableFormats = true;
50223         }
50224         tpls.body.compile();
50225
50226         if(!tpls.row){
50227             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
50228             tpls.row.disableFormats = true;
50229         }
50230         tpls.row.compile();
50231
50232         if(!tpls.cell){
50233             tpls.cell = new Roo.Template(
50234                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
50235                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
50236                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
50237                 "</td>"
50238             );
50239             tpls.cell.disableFormats = true;
50240         }
50241         tpls.cell.compile();
50242
50243         this.templates = tpls;
50244     },
50245
50246     // remap these for backwards compat
50247     onColWidthChange : function(){
50248         this.updateColumns.apply(this, arguments);
50249     },
50250     onHeaderChange : function(){
50251         this.updateHeaders.apply(this, arguments);
50252     }, 
50253     onHiddenChange : function(){
50254         this.handleHiddenChange.apply(this, arguments);
50255     },
50256     onColumnMove : function(){
50257         this.handleColumnMove.apply(this, arguments);
50258     },
50259     onColumnLock : function(){
50260         this.handleLockChange.apply(this, arguments);
50261     },
50262
50263     onDataChange : function(){
50264         this.refresh();
50265         this.updateHeaderSortState();
50266     },
50267
50268     onClear : function(){
50269         this.refresh();
50270     },
50271
50272     onUpdate : function(ds, record){
50273         this.refreshRow(record);
50274     },
50275
50276     refreshRow : function(record){
50277         var ds = this.ds, index;
50278         if(typeof record == 'number'){
50279             index = record;
50280             record = ds.getAt(index);
50281         }else{
50282             index = ds.indexOf(record);
50283         }
50284         this.insertRows(ds, index, index, true);
50285         this.onRemove(ds, record, index+1, true);
50286         this.syncRowHeights(index, index);
50287         this.layout();
50288         this.fireEvent("rowupdated", this, index, record);
50289     },
50290
50291     onAdd : function(ds, records, index){
50292         this.insertRows(ds, index, index + (records.length-1));
50293     },
50294
50295     onRemove : function(ds, record, index, isUpdate){
50296         if(isUpdate !== true){
50297             this.fireEvent("beforerowremoved", this, index, record);
50298         }
50299         var bt = this.getBodyTable(), lt = this.getLockedTable();
50300         if(bt.rows[index]){
50301             bt.firstChild.removeChild(bt.rows[index]);
50302         }
50303         if(lt.rows[index]){
50304             lt.firstChild.removeChild(lt.rows[index]);
50305         }
50306         if(isUpdate !== true){
50307             this.stripeRows(index);
50308             this.syncRowHeights(index, index);
50309             this.layout();
50310             this.fireEvent("rowremoved", this, index, record);
50311         }
50312     },
50313
50314     onLoad : function(){
50315         this.scrollToTop();
50316     },
50317
50318     /**
50319      * Scrolls the grid to the top
50320      */
50321     scrollToTop : function(){
50322         if(this.scroller){
50323             this.scroller.dom.scrollTop = 0;
50324             this.syncScroll();
50325         }
50326     },
50327
50328     /**
50329      * Gets a panel in the header of the grid that can be used for toolbars etc.
50330      * After modifying the contents of this panel a call to grid.autoSize() may be
50331      * required to register any changes in size.
50332      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
50333      * @return Roo.Element
50334      */
50335     getHeaderPanel : function(doShow){
50336         if(doShow){
50337             this.headerPanel.show();
50338         }
50339         return this.headerPanel;
50340     },
50341
50342     /**
50343      * Gets a panel in the footer of the grid that can be used for toolbars etc.
50344      * After modifying the contents of this panel a call to grid.autoSize() may be
50345      * required to register any changes in size.
50346      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
50347      * @return Roo.Element
50348      */
50349     getFooterPanel : function(doShow){
50350         if(doShow){
50351             this.footerPanel.show();
50352         }
50353         return this.footerPanel;
50354     },
50355
50356     initElements : function(){
50357         var E = Roo.Element;
50358         var el = this.grid.getGridEl().dom.firstChild;
50359         var cs = el.childNodes;
50360
50361         this.el = new E(el);
50362         
50363          this.focusEl = new E(el.firstChild);
50364         this.focusEl.swallowEvent("click", true);
50365         
50366         this.headerPanel = new E(cs[1]);
50367         this.headerPanel.enableDisplayMode("block");
50368
50369         this.scroller = new E(cs[2]);
50370         this.scrollSizer = new E(this.scroller.dom.firstChild);
50371
50372         this.lockedWrap = new E(cs[3]);
50373         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
50374         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
50375
50376         this.mainWrap = new E(cs[4]);
50377         this.mainHd = new E(this.mainWrap.dom.firstChild);
50378         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
50379
50380         this.footerPanel = new E(cs[5]);
50381         this.footerPanel.enableDisplayMode("block");
50382
50383         this.resizeProxy = new E(cs[6]);
50384
50385         this.headerSelector = String.format(
50386            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
50387            this.lockedHd.id, this.mainHd.id
50388         );
50389
50390         this.splitterSelector = String.format(
50391            '#{0} div.x-grid-split, #{1} div.x-grid-split',
50392            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
50393         );
50394     },
50395     idToCssName : function(s)
50396     {
50397         return s.replace(/[^a-z0-9]+/ig, '-');
50398     },
50399
50400     getHeaderCell : function(index){
50401         return Roo.DomQuery.select(this.headerSelector)[index];
50402     },
50403
50404     getHeaderCellMeasure : function(index){
50405         return this.getHeaderCell(index).firstChild;
50406     },
50407
50408     getHeaderCellText : function(index){
50409         return this.getHeaderCell(index).firstChild.firstChild;
50410     },
50411
50412     getLockedTable : function(){
50413         return this.lockedBody.dom.firstChild;
50414     },
50415
50416     getBodyTable : function(){
50417         return this.mainBody.dom.firstChild;
50418     },
50419
50420     getLockedRow : function(index){
50421         return this.getLockedTable().rows[index];
50422     },
50423
50424     getRow : function(index){
50425         return this.getBodyTable().rows[index];
50426     },
50427
50428     getRowComposite : function(index){
50429         if(!this.rowEl){
50430             this.rowEl = new Roo.CompositeElementLite();
50431         }
50432         var els = [], lrow, mrow;
50433         if(lrow = this.getLockedRow(index)){
50434             els.push(lrow);
50435         }
50436         if(mrow = this.getRow(index)){
50437             els.push(mrow);
50438         }
50439         this.rowEl.elements = els;
50440         return this.rowEl;
50441     },
50442     /**
50443      * Gets the 'td' of the cell
50444      * 
50445      * @param {Integer} rowIndex row to select
50446      * @param {Integer} colIndex column to select
50447      * 
50448      * @return {Object} 
50449      */
50450     getCell : function(rowIndex, colIndex){
50451         var locked = this.cm.getLockedCount();
50452         var source;
50453         if(colIndex < locked){
50454             source = this.lockedBody.dom.firstChild;
50455         }else{
50456             source = this.mainBody.dom.firstChild;
50457             colIndex -= locked;
50458         }
50459         return source.rows[rowIndex].childNodes[colIndex];
50460     },
50461
50462     getCellText : function(rowIndex, colIndex){
50463         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
50464     },
50465
50466     getCellBox : function(cell){
50467         var b = this.fly(cell).getBox();
50468         if(Roo.isOpera){ // opera fails to report the Y
50469             b.y = cell.offsetTop + this.mainBody.getY();
50470         }
50471         return b;
50472     },
50473
50474     getCellIndex : function(cell){
50475         var id = String(cell.className).match(this.cellRE);
50476         if(id){
50477             return parseInt(id[1], 10);
50478         }
50479         return 0;
50480     },
50481
50482     findHeaderIndex : function(n){
50483         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
50484         return r ? this.getCellIndex(r) : false;
50485     },
50486
50487     findHeaderCell : function(n){
50488         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
50489         return r ? r : false;
50490     },
50491
50492     findRowIndex : function(n){
50493         if(!n){
50494             return false;
50495         }
50496         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
50497         return r ? r.rowIndex : false;
50498     },
50499
50500     findCellIndex : function(node){
50501         var stop = this.el.dom;
50502         while(node && node != stop){
50503             if(this.findRE.test(node.className)){
50504                 return this.getCellIndex(node);
50505             }
50506             node = node.parentNode;
50507         }
50508         return false;
50509     },
50510
50511     getColumnId : function(index){
50512         return this.cm.getColumnId(index);
50513     },
50514
50515     getSplitters : function()
50516     {
50517         if(this.splitterSelector){
50518            return Roo.DomQuery.select(this.splitterSelector);
50519         }else{
50520             return null;
50521       }
50522     },
50523
50524     getSplitter : function(index){
50525         return this.getSplitters()[index];
50526     },
50527
50528     onRowOver : function(e, t){
50529         var row;
50530         if((row = this.findRowIndex(t)) !== false){
50531             this.getRowComposite(row).addClass("x-grid-row-over");
50532         }
50533     },
50534
50535     onRowOut : function(e, t){
50536         var row;
50537         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
50538             this.getRowComposite(row).removeClass("x-grid-row-over");
50539         }
50540     },
50541
50542     renderHeaders : function(){
50543         var cm = this.cm;
50544         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
50545         var cb = [], lb = [], sb = [], lsb = [], p = {};
50546         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50547             p.cellId = "x-grid-hd-0-" + i;
50548             p.splitId = "x-grid-csplit-0-" + i;
50549             p.id = cm.getColumnId(i);
50550             p.title = cm.getColumnTooltip(i) || "";
50551             p.value = cm.getColumnHeader(i) || "";
50552             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
50553             if(!cm.isLocked(i)){
50554                 cb[cb.length] = ct.apply(p);
50555                 sb[sb.length] = st.apply(p);
50556             }else{
50557                 lb[lb.length] = ct.apply(p);
50558                 lsb[lsb.length] = st.apply(p);
50559             }
50560         }
50561         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
50562                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
50563     },
50564
50565     updateHeaders : function(){
50566         var html = this.renderHeaders();
50567         this.lockedHd.update(html[0]);
50568         this.mainHd.update(html[1]);
50569     },
50570
50571     /**
50572      * Focuses the specified row.
50573      * @param {Number} row The row index
50574      */
50575     focusRow : function(row)
50576     {
50577         //Roo.log('GridView.focusRow');
50578         var x = this.scroller.dom.scrollLeft;
50579         this.focusCell(row, 0, false);
50580         this.scroller.dom.scrollLeft = x;
50581     },
50582
50583     /**
50584      * Focuses the specified cell.
50585      * @param {Number} row The row index
50586      * @param {Number} col The column index
50587      * @param {Boolean} hscroll false to disable horizontal scrolling
50588      */
50589     focusCell : function(row, col, hscroll)
50590     {
50591         //Roo.log('GridView.focusCell');
50592         var el = this.ensureVisible(row, col, hscroll);
50593         this.focusEl.alignTo(el, "tl-tl");
50594         if(Roo.isGecko){
50595             this.focusEl.focus();
50596         }else{
50597             this.focusEl.focus.defer(1, this.focusEl);
50598         }
50599     },
50600
50601     /**
50602      * Scrolls the specified cell into view
50603      * @param {Number} row The row index
50604      * @param {Number} col The column index
50605      * @param {Boolean} hscroll false to disable horizontal scrolling
50606      */
50607     ensureVisible : function(row, col, hscroll)
50608     {
50609         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
50610         //return null; //disable for testing.
50611         if(typeof row != "number"){
50612             row = row.rowIndex;
50613         }
50614         if(row < 0 && row >= this.ds.getCount()){
50615             return  null;
50616         }
50617         col = (col !== undefined ? col : 0);
50618         var cm = this.grid.colModel;
50619         while(cm.isHidden(col)){
50620             col++;
50621         }
50622
50623         var el = this.getCell(row, col);
50624         if(!el){
50625             return null;
50626         }
50627         var c = this.scroller.dom;
50628
50629         var ctop = parseInt(el.offsetTop, 10);
50630         var cleft = parseInt(el.offsetLeft, 10);
50631         var cbot = ctop + el.offsetHeight;
50632         var cright = cleft + el.offsetWidth;
50633         
50634         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
50635         var stop = parseInt(c.scrollTop, 10);
50636         var sleft = parseInt(c.scrollLeft, 10);
50637         var sbot = stop + ch;
50638         var sright = sleft + c.clientWidth;
50639         /*
50640         Roo.log('GridView.ensureVisible:' +
50641                 ' ctop:' + ctop +
50642                 ' c.clientHeight:' + c.clientHeight +
50643                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
50644                 ' stop:' + stop +
50645                 ' cbot:' + cbot +
50646                 ' sbot:' + sbot +
50647                 ' ch:' + ch  
50648                 );
50649         */
50650         if(ctop < stop){
50651              c.scrollTop = ctop;
50652             //Roo.log("set scrolltop to ctop DISABLE?");
50653         }else if(cbot > sbot){
50654             //Roo.log("set scrolltop to cbot-ch");
50655             c.scrollTop = cbot-ch;
50656         }
50657         
50658         if(hscroll !== false){
50659             if(cleft < sleft){
50660                 c.scrollLeft = cleft;
50661             }else if(cright > sright){
50662                 c.scrollLeft = cright-c.clientWidth;
50663             }
50664         }
50665          
50666         return el;
50667     },
50668
50669     updateColumns : function(){
50670         this.grid.stopEditing();
50671         var cm = this.grid.colModel, colIds = this.getColumnIds();
50672         //var totalWidth = cm.getTotalWidth();
50673         var pos = 0;
50674         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50675             //if(cm.isHidden(i)) continue;
50676             var w = cm.getColumnWidth(i);
50677             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
50678             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
50679         }
50680         this.updateSplitters();
50681     },
50682
50683     generateRules : function(cm){
50684         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
50685         Roo.util.CSS.removeStyleSheet(rulesId);
50686         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50687             var cid = cm.getColumnId(i);
50688             var align = '';
50689             if(cm.config[i].align){
50690                 align = 'text-align:'+cm.config[i].align+';';
50691             }
50692             var hidden = '';
50693             if(cm.isHidden(i)){
50694                 hidden = 'display:none;';
50695             }
50696             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
50697             ruleBuf.push(
50698                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
50699                     this.hdSelector, cid, " {\n", align, width, "}\n",
50700                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
50701                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
50702         }
50703         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
50704     },
50705
50706     updateSplitters : function(){
50707         var cm = this.cm, s = this.getSplitters();
50708         if(s){ // splitters not created yet
50709             var pos = 0, locked = true;
50710             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50711                 if(cm.isHidden(i)) continue;
50712                 var w = cm.getColumnWidth(i); // make sure it's a number
50713                 if(!cm.isLocked(i) && locked){
50714                     pos = 0;
50715                     locked = false;
50716                 }
50717                 pos += w;
50718                 s[i].style.left = (pos-this.splitOffset) + "px";
50719             }
50720         }
50721     },
50722
50723     handleHiddenChange : function(colModel, colIndex, hidden){
50724         if(hidden){
50725             this.hideColumn(colIndex);
50726         }else{
50727             this.unhideColumn(colIndex);
50728         }
50729     },
50730
50731     hideColumn : function(colIndex){
50732         var cid = this.getColumnId(colIndex);
50733         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
50734         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
50735         if(Roo.isSafari){
50736             this.updateHeaders();
50737         }
50738         this.updateSplitters();
50739         this.layout();
50740     },
50741
50742     unhideColumn : function(colIndex){
50743         var cid = this.getColumnId(colIndex);
50744         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
50745         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
50746
50747         if(Roo.isSafari){
50748             this.updateHeaders();
50749         }
50750         this.updateSplitters();
50751         this.layout();
50752     },
50753
50754     insertRows : function(dm, firstRow, lastRow, isUpdate){
50755         if(firstRow == 0 && lastRow == dm.getCount()-1){
50756             this.refresh();
50757         }else{
50758             if(!isUpdate){
50759                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
50760             }
50761             var s = this.getScrollState();
50762             var markup = this.renderRows(firstRow, lastRow);
50763             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
50764             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
50765             this.restoreScroll(s);
50766             if(!isUpdate){
50767                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
50768                 this.syncRowHeights(firstRow, lastRow);
50769                 this.stripeRows(firstRow);
50770                 this.layout();
50771             }
50772         }
50773     },
50774
50775     bufferRows : function(markup, target, index){
50776         var before = null, trows = target.rows, tbody = target.tBodies[0];
50777         if(index < trows.length){
50778             before = trows[index];
50779         }
50780         var b = document.createElement("div");
50781         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
50782         var rows = b.firstChild.rows;
50783         for(var i = 0, len = rows.length; i < len; i++){
50784             if(before){
50785                 tbody.insertBefore(rows[0], before);
50786             }else{
50787                 tbody.appendChild(rows[0]);
50788             }
50789         }
50790         b.innerHTML = "";
50791         b = null;
50792     },
50793
50794     deleteRows : function(dm, firstRow, lastRow){
50795         if(dm.getRowCount()<1){
50796             this.fireEvent("beforerefresh", this);
50797             this.mainBody.update("");
50798             this.lockedBody.update("");
50799             this.fireEvent("refresh", this);
50800         }else{
50801             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
50802             var bt = this.getBodyTable();
50803             var tbody = bt.firstChild;
50804             var rows = bt.rows;
50805             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
50806                 tbody.removeChild(rows[firstRow]);
50807             }
50808             this.stripeRows(firstRow);
50809             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
50810         }
50811     },
50812
50813     updateRows : function(dataSource, firstRow, lastRow){
50814         var s = this.getScrollState();
50815         this.refresh();
50816         this.restoreScroll(s);
50817     },
50818
50819     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
50820         if(!noRefresh){
50821            this.refresh();
50822         }
50823         this.updateHeaderSortState();
50824     },
50825
50826     getScrollState : function(){
50827         
50828         var sb = this.scroller.dom;
50829         return {left: sb.scrollLeft, top: sb.scrollTop};
50830     },
50831
50832     stripeRows : function(startRow){
50833         if(!this.grid.stripeRows || this.ds.getCount() < 1){
50834             return;
50835         }
50836         startRow = startRow || 0;
50837         var rows = this.getBodyTable().rows;
50838         var lrows = this.getLockedTable().rows;
50839         var cls = ' x-grid-row-alt ';
50840         for(var i = startRow, len = rows.length; i < len; i++){
50841             var row = rows[i], lrow = lrows[i];
50842             var isAlt = ((i+1) % 2 == 0);
50843             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
50844             if(isAlt == hasAlt){
50845                 continue;
50846             }
50847             if(isAlt){
50848                 row.className += " x-grid-row-alt";
50849             }else{
50850                 row.className = row.className.replace("x-grid-row-alt", "");
50851             }
50852             if(lrow){
50853                 lrow.className = row.className;
50854             }
50855         }
50856     },
50857
50858     restoreScroll : function(state){
50859         //Roo.log('GridView.restoreScroll');
50860         var sb = this.scroller.dom;
50861         sb.scrollLeft = state.left;
50862         sb.scrollTop = state.top;
50863         this.syncScroll();
50864     },
50865
50866     syncScroll : function(){
50867         //Roo.log('GridView.syncScroll');
50868         var sb = this.scroller.dom;
50869         var sh = this.mainHd.dom;
50870         var bs = this.mainBody.dom;
50871         var lv = this.lockedBody.dom;
50872         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
50873         lv.scrollTop = bs.scrollTop = sb.scrollTop;
50874     },
50875
50876     handleScroll : function(e){
50877         this.syncScroll();
50878         var sb = this.scroller.dom;
50879         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
50880         e.stopEvent();
50881     },
50882
50883     handleWheel : function(e){
50884         var d = e.getWheelDelta();
50885         this.scroller.dom.scrollTop -= d*22;
50886         // set this here to prevent jumpy scrolling on large tables
50887         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
50888         e.stopEvent();
50889     },
50890
50891     renderRows : function(startRow, endRow){
50892         // pull in all the crap needed to render rows
50893         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
50894         var colCount = cm.getColumnCount();
50895
50896         if(ds.getCount() < 1){
50897             return ["", ""];
50898         }
50899
50900         // build a map for all the columns
50901         var cs = [];
50902         for(var i = 0; i < colCount; i++){
50903             var name = cm.getDataIndex(i);
50904             cs[i] = {
50905                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
50906                 renderer : cm.getRenderer(i),
50907                 id : cm.getColumnId(i),
50908                 locked : cm.isLocked(i)
50909             };
50910         }
50911
50912         startRow = startRow || 0;
50913         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
50914
50915         // records to render
50916         var rs = ds.getRange(startRow, endRow);
50917
50918         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
50919     },
50920
50921     // As much as I hate to duplicate code, this was branched because FireFox really hates
50922     // [].join("") on strings. The performance difference was substantial enough to
50923     // branch this function
50924     doRender : Roo.isGecko ?
50925             function(cs, rs, ds, startRow, colCount, stripe){
50926                 var ts = this.templates, ct = ts.cell, rt = ts.row;
50927                 // buffers
50928                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
50929                 
50930                 var hasListener = this.grid.hasListener('rowclass');
50931                 var rowcfg = {};
50932                 for(var j = 0, len = rs.length; j < len; j++){
50933                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
50934                     for(var i = 0; i < colCount; i++){
50935                         c = cs[i];
50936                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
50937                         p.id = c.id;
50938                         p.css = p.attr = "";
50939                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
50940                         if(p.value == undefined || p.value === "") p.value = "&#160;";
50941                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
50942                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
50943                         }
50944                         var markup = ct.apply(p);
50945                         if(!c.locked){
50946                             cb+= markup;
50947                         }else{
50948                             lcb+= markup;
50949                         }
50950                     }
50951                     var alt = [];
50952                     if(stripe && ((rowIndex+1) % 2 == 0)){
50953                         alt.push("x-grid-row-alt")
50954                     }
50955                     if(r.dirty){
50956                         alt.push(  " x-grid-dirty-row");
50957                     }
50958                     rp.cells = lcb;
50959                     if(this.getRowClass){
50960                         alt.push(this.getRowClass(r, rowIndex));
50961                     }
50962                     if (hasListener) {
50963                         rowcfg = {
50964                              
50965                             record: r,
50966                             rowIndex : rowIndex,
50967                             rowClass : ''
50968                         }
50969                         this.grid.fireEvent('rowclass', this, rowcfg);
50970                         alt.push(rowcfg.rowClass);
50971                     }
50972                     rp.alt = alt.join(" ");
50973                     lbuf+= rt.apply(rp);
50974                     rp.cells = cb;
50975                     buf+=  rt.apply(rp);
50976                 }
50977                 return [lbuf, buf];
50978             } :
50979             function(cs, rs, ds, startRow, colCount, stripe){
50980                 var ts = this.templates, ct = ts.cell, rt = ts.row;
50981                 // buffers
50982                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
50983                 var hasListener = this.grid.hasListener('rowclass');
50984  
50985                 var rowcfg = {};
50986                 for(var j = 0, len = rs.length; j < len; j++){
50987                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
50988                     for(var i = 0; i < colCount; i++){
50989                         c = cs[i];
50990                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
50991                         p.id = c.id;
50992                         p.css = p.attr = "";
50993                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
50994                         if(p.value == undefined || p.value === "") p.value = "&#160;";
50995                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
50996                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
50997                         }
50998                         
50999                         var markup = ct.apply(p);
51000                         if(!c.locked){
51001                             cb[cb.length] = markup;
51002                         }else{
51003                             lcb[lcb.length] = markup;
51004                         }
51005                     }
51006                     var alt = [];
51007                     if(stripe && ((rowIndex+1) % 2 == 0)){
51008                         alt.push( "x-grid-row-alt");
51009                     }
51010                     if(r.dirty){
51011                         alt.push(" x-grid-dirty-row");
51012                     }
51013                     rp.cells = lcb;
51014                     if(this.getRowClass){
51015                         alt.push( this.getRowClass(r, rowIndex));
51016                     }
51017                     if (hasListener) {
51018                         rowcfg = {
51019                              
51020                             record: r,
51021                             rowIndex : rowIndex,
51022                             rowClass : ''
51023                         }
51024                         this.grid.fireEvent('rowclass', this, rowcfg);
51025                         alt.push(rowcfg.rowClass);
51026                     }
51027                     rp.alt = alt.join(" ");
51028                     rp.cells = lcb.join("");
51029                     lbuf[lbuf.length] = rt.apply(rp);
51030                     rp.cells = cb.join("");
51031                     buf[buf.length] =  rt.apply(rp);
51032                 }
51033                 return [lbuf.join(""), buf.join("")];
51034             },
51035
51036     renderBody : function(){
51037         var markup = this.renderRows();
51038         var bt = this.templates.body;
51039         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
51040     },
51041
51042     /**
51043      * Refreshes the grid
51044      * @param {Boolean} headersToo
51045      */
51046     refresh : function(headersToo){
51047         this.fireEvent("beforerefresh", this);
51048         this.grid.stopEditing();
51049         var result = this.renderBody();
51050         this.lockedBody.update(result[0]);
51051         this.mainBody.update(result[1]);
51052         if(headersToo === true){
51053             this.updateHeaders();
51054             this.updateColumns();
51055             this.updateSplitters();
51056             this.updateHeaderSortState();
51057         }
51058         this.syncRowHeights();
51059         this.layout();
51060         this.fireEvent("refresh", this);
51061     },
51062
51063     handleColumnMove : function(cm, oldIndex, newIndex){
51064         this.indexMap = null;
51065         var s = this.getScrollState();
51066         this.refresh(true);
51067         this.restoreScroll(s);
51068         this.afterMove(newIndex);
51069     },
51070
51071     afterMove : function(colIndex){
51072         if(this.enableMoveAnim && Roo.enableFx){
51073             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
51074         }
51075         // if multisort - fix sortOrder, and reload..
51076         if (this.grid.dataSource.multiSort) {
51077             // the we can call sort again..
51078             var dm = this.grid.dataSource;
51079             var cm = this.grid.colModel;
51080             var so = [];
51081             for(var i = 0; i < cm.config.length; i++ ) {
51082                 
51083                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
51084                     continue; // dont' bother, it's not in sort list or being set.
51085                 }
51086                 
51087                 so.push(cm.config[i].dataIndex);
51088             };
51089             dm.sortOrder = so;
51090             dm.load(dm.lastOptions);
51091             
51092             
51093         }
51094         
51095     },
51096
51097     updateCell : function(dm, rowIndex, dataIndex){
51098         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
51099         if(typeof colIndex == "undefined"){ // not present in grid
51100             return;
51101         }
51102         var cm = this.grid.colModel;
51103         var cell = this.getCell(rowIndex, colIndex);
51104         var cellText = this.getCellText(rowIndex, colIndex);
51105
51106         var p = {
51107             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
51108             id : cm.getColumnId(colIndex),
51109             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
51110         };
51111         var renderer = cm.getRenderer(colIndex);
51112         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
51113         if(typeof val == "undefined" || val === "") val = "&#160;";
51114         cellText.innerHTML = val;
51115         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
51116         this.syncRowHeights(rowIndex, rowIndex);
51117     },
51118
51119     calcColumnWidth : function(colIndex, maxRowsToMeasure){
51120         var maxWidth = 0;
51121         if(this.grid.autoSizeHeaders){
51122             var h = this.getHeaderCellMeasure(colIndex);
51123             maxWidth = Math.max(maxWidth, h.scrollWidth);
51124         }
51125         var tb, index;
51126         if(this.cm.isLocked(colIndex)){
51127             tb = this.getLockedTable();
51128             index = colIndex;
51129         }else{
51130             tb = this.getBodyTable();
51131             index = colIndex - this.cm.getLockedCount();
51132         }
51133         if(tb && tb.rows){
51134             var rows = tb.rows;
51135             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
51136             for(var i = 0; i < stopIndex; i++){
51137                 var cell = rows[i].childNodes[index].firstChild;
51138                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
51139             }
51140         }
51141         return maxWidth + /*margin for error in IE*/ 5;
51142     },
51143     /**
51144      * Autofit a column to its content.
51145      * @param {Number} colIndex
51146      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
51147      */
51148      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
51149          if(this.cm.isHidden(colIndex)){
51150              return; // can't calc a hidden column
51151          }
51152         if(forceMinSize){
51153             var cid = this.cm.getColumnId(colIndex);
51154             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
51155            if(this.grid.autoSizeHeaders){
51156                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
51157            }
51158         }
51159         var newWidth = this.calcColumnWidth(colIndex);
51160         this.cm.setColumnWidth(colIndex,
51161             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
51162         if(!suppressEvent){
51163             this.grid.fireEvent("columnresize", colIndex, newWidth);
51164         }
51165     },
51166
51167     /**
51168      * Autofits all columns to their content and then expands to fit any extra space in the grid
51169      */
51170      autoSizeColumns : function(){
51171         var cm = this.grid.colModel;
51172         var colCount = cm.getColumnCount();
51173         for(var i = 0; i < colCount; i++){
51174             this.autoSizeColumn(i, true, true);
51175         }
51176         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
51177             this.fitColumns();
51178         }else{
51179             this.updateColumns();
51180             this.layout();
51181         }
51182     },
51183
51184     /**
51185      * Autofits all columns to the grid's width proportionate with their current size
51186      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
51187      */
51188     fitColumns : function(reserveScrollSpace){
51189         var cm = this.grid.colModel;
51190         var colCount = cm.getColumnCount();
51191         var cols = [];
51192         var width = 0;
51193         var i, w;
51194         for (i = 0; i < colCount; i++){
51195             if(!cm.isHidden(i) && !cm.isFixed(i)){
51196                 w = cm.getColumnWidth(i);
51197                 cols.push(i);
51198                 cols.push(w);
51199                 width += w;
51200             }
51201         }
51202         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
51203         if(reserveScrollSpace){
51204             avail -= 17;
51205         }
51206         var frac = (avail - cm.getTotalWidth())/width;
51207         while (cols.length){
51208             w = cols.pop();
51209             i = cols.pop();
51210             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
51211         }
51212         this.updateColumns();
51213         this.layout();
51214     },
51215
51216     onRowSelect : function(rowIndex){
51217         var row = this.getRowComposite(rowIndex);
51218         row.addClass("x-grid-row-selected");
51219     },
51220
51221     onRowDeselect : function(rowIndex){
51222         var row = this.getRowComposite(rowIndex);
51223         row.removeClass("x-grid-row-selected");
51224     },
51225
51226     onCellSelect : function(row, col){
51227         var cell = this.getCell(row, col);
51228         if(cell){
51229             Roo.fly(cell).addClass("x-grid-cell-selected");
51230         }
51231     },
51232
51233     onCellDeselect : function(row, col){
51234         var cell = this.getCell(row, col);
51235         if(cell){
51236             Roo.fly(cell).removeClass("x-grid-cell-selected");
51237         }
51238     },
51239
51240     updateHeaderSortState : function(){
51241         
51242         // sort state can be single { field: xxx, direction : yyy}
51243         // or   { xxx=>ASC , yyy : DESC ..... }
51244         
51245         var mstate = {};
51246         if (!this.ds.multiSort) { 
51247             var state = this.ds.getSortState();
51248             if(!state){
51249                 return;
51250             }
51251             mstate[state.field] = state.direction;
51252             // FIXME... - this is not used here.. but might be elsewhere..
51253             this.sortState = state;
51254             
51255         } else {
51256             mstate = this.ds.sortToggle;
51257         }
51258         //remove existing sort classes..
51259         
51260         var sc = this.sortClasses;
51261         var hds = this.el.select(this.headerSelector).removeClass(sc);
51262         
51263         for(var f in mstate) {
51264         
51265             var sortColumn = this.cm.findColumnIndex(f);
51266             
51267             if(sortColumn != -1){
51268                 var sortDir = mstate[f];        
51269                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
51270             }
51271         }
51272         
51273          
51274         
51275     },
51276
51277
51278     handleHeaderClick : function(g, index){
51279         if(this.headersDisabled){
51280             return;
51281         }
51282         var dm = g.dataSource, cm = g.colModel;
51283         if(!cm.isSortable(index)){
51284             return;
51285         }
51286         g.stopEditing();
51287         
51288         if (dm.multiSort) {
51289             // update the sortOrder
51290             var so = [];
51291             for(var i = 0; i < cm.config.length; i++ ) {
51292                 
51293                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
51294                     continue; // dont' bother, it's not in sort list or being set.
51295                 }
51296                 
51297                 so.push(cm.config[i].dataIndex);
51298             };
51299             dm.sortOrder = so;
51300         }
51301         
51302         
51303         dm.sort(cm.getDataIndex(index));
51304     },
51305
51306
51307     destroy : function(){
51308         if(this.colMenu){
51309             this.colMenu.removeAll();
51310             Roo.menu.MenuMgr.unregister(this.colMenu);
51311             this.colMenu.getEl().remove();
51312             delete this.colMenu;
51313         }
51314         if(this.hmenu){
51315             this.hmenu.removeAll();
51316             Roo.menu.MenuMgr.unregister(this.hmenu);
51317             this.hmenu.getEl().remove();
51318             delete this.hmenu;
51319         }
51320         if(this.grid.enableColumnMove){
51321             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
51322             if(dds){
51323                 for(var dd in dds){
51324                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
51325                         var elid = dds[dd].dragElId;
51326                         dds[dd].unreg();
51327                         Roo.get(elid).remove();
51328                     } else if(dds[dd].config.isTarget){
51329                         dds[dd].proxyTop.remove();
51330                         dds[dd].proxyBottom.remove();
51331                         dds[dd].unreg();
51332                     }
51333                     if(Roo.dd.DDM.locationCache[dd]){
51334                         delete Roo.dd.DDM.locationCache[dd];
51335                     }
51336                 }
51337                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
51338             }
51339         }
51340         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
51341         this.bind(null, null);
51342         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
51343     },
51344
51345     handleLockChange : function(){
51346         this.refresh(true);
51347     },
51348
51349     onDenyColumnLock : function(){
51350
51351     },
51352
51353     onDenyColumnHide : function(){
51354
51355     },
51356
51357     handleHdMenuClick : function(item){
51358         var index = this.hdCtxIndex;
51359         var cm = this.cm, ds = this.ds;
51360         switch(item.id){
51361             case "asc":
51362                 ds.sort(cm.getDataIndex(index), "ASC");
51363                 break;
51364             case "desc":
51365                 ds.sort(cm.getDataIndex(index), "DESC");
51366                 break;
51367             case "lock":
51368                 var lc = cm.getLockedCount();
51369                 if(cm.getColumnCount(true) <= lc+1){
51370                     this.onDenyColumnLock();
51371                     return;
51372                 }
51373                 if(lc != index){
51374                     cm.setLocked(index, true, true);
51375                     cm.moveColumn(index, lc);
51376                     this.grid.fireEvent("columnmove", index, lc);
51377                 }else{
51378                     cm.setLocked(index, true);
51379                 }
51380             break;
51381             case "unlock":
51382                 var lc = cm.getLockedCount();
51383                 if((lc-1) != index){
51384                     cm.setLocked(index, false, true);
51385                     cm.moveColumn(index, lc-1);
51386                     this.grid.fireEvent("columnmove", index, lc-1);
51387                 }else{
51388                     cm.setLocked(index, false);
51389                 }
51390             break;
51391             default:
51392                 index = cm.getIndexById(item.id.substr(4));
51393                 if(index != -1){
51394                     if(item.checked && cm.getColumnCount(true) <= 1){
51395                         this.onDenyColumnHide();
51396                         return false;
51397                     }
51398                     cm.setHidden(index, item.checked);
51399                 }
51400         }
51401         return true;
51402     },
51403
51404     beforeColMenuShow : function(){
51405         var cm = this.cm,  colCount = cm.getColumnCount();
51406         this.colMenu.removeAll();
51407         for(var i = 0; i < colCount; i++){
51408             this.colMenu.add(new Roo.menu.CheckItem({
51409                 id: "col-"+cm.getColumnId(i),
51410                 text: cm.getColumnHeader(i),
51411                 checked: !cm.isHidden(i),
51412                 hideOnClick:false
51413             }));
51414         }
51415     },
51416
51417     handleHdCtx : function(g, index, e){
51418         e.stopEvent();
51419         var hd = this.getHeaderCell(index);
51420         this.hdCtxIndex = index;
51421         var ms = this.hmenu.items, cm = this.cm;
51422         ms.get("asc").setDisabled(!cm.isSortable(index));
51423         ms.get("desc").setDisabled(!cm.isSortable(index));
51424         if(this.grid.enableColLock !== false){
51425             ms.get("lock").setDisabled(cm.isLocked(index));
51426             ms.get("unlock").setDisabled(!cm.isLocked(index));
51427         }
51428         this.hmenu.show(hd, "tl-bl");
51429     },
51430
51431     handleHdOver : function(e){
51432         var hd = this.findHeaderCell(e.getTarget());
51433         if(hd && !this.headersDisabled){
51434             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
51435                this.fly(hd).addClass("x-grid-hd-over");
51436             }
51437         }
51438     },
51439
51440     handleHdOut : function(e){
51441         var hd = this.findHeaderCell(e.getTarget());
51442         if(hd){
51443             this.fly(hd).removeClass("x-grid-hd-over");
51444         }
51445     },
51446
51447     handleSplitDblClick : function(e, t){
51448         var i = this.getCellIndex(t);
51449         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
51450             this.autoSizeColumn(i, true);
51451             this.layout();
51452         }
51453     },
51454
51455     render : function(){
51456
51457         var cm = this.cm;
51458         var colCount = cm.getColumnCount();
51459
51460         if(this.grid.monitorWindowResize === true){
51461             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51462         }
51463         var header = this.renderHeaders();
51464         var body = this.templates.body.apply({rows:""});
51465         var html = this.templates.master.apply({
51466             lockedBody: body,
51467             body: body,
51468             lockedHeader: header[0],
51469             header: header[1]
51470         });
51471
51472         //this.updateColumns();
51473
51474         this.grid.getGridEl().dom.innerHTML = html;
51475
51476         this.initElements();
51477         
51478         // a kludge to fix the random scolling effect in webkit
51479         this.el.on("scroll", function() {
51480             this.el.dom.scrollTop=0; // hopefully not recursive..
51481         },this);
51482
51483         this.scroller.on("scroll", this.handleScroll, this);
51484         this.lockedBody.on("mousewheel", this.handleWheel, this);
51485         this.mainBody.on("mousewheel", this.handleWheel, this);
51486
51487         this.mainHd.on("mouseover", this.handleHdOver, this);
51488         this.mainHd.on("mouseout", this.handleHdOut, this);
51489         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
51490                 {delegate: "."+this.splitClass});
51491
51492         this.lockedHd.on("mouseover", this.handleHdOver, this);
51493         this.lockedHd.on("mouseout", this.handleHdOut, this);
51494         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
51495                 {delegate: "."+this.splitClass});
51496
51497         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
51498             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
51499         }
51500
51501         this.updateSplitters();
51502
51503         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
51504             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
51505             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
51506         }
51507
51508         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
51509             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
51510             this.hmenu.add(
51511                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
51512                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
51513             );
51514             if(this.grid.enableColLock !== false){
51515                 this.hmenu.add('-',
51516                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
51517                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
51518                 );
51519             }
51520             if(this.grid.enableColumnHide !== false){
51521
51522                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
51523                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
51524                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
51525
51526                 this.hmenu.add('-',
51527                     {id:"columns", text: this.columnsText, menu: this.colMenu}
51528                 );
51529             }
51530             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
51531
51532             this.grid.on("headercontextmenu", this.handleHdCtx, this);
51533         }
51534
51535         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
51536             this.dd = new Roo.grid.GridDragZone(this.grid, {
51537                 ddGroup : this.grid.ddGroup || 'GridDD'
51538             });
51539             
51540         }
51541
51542         /*
51543         for(var i = 0; i < colCount; i++){
51544             if(cm.isHidden(i)){
51545                 this.hideColumn(i);
51546             }
51547             if(cm.config[i].align){
51548                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
51549                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
51550             }
51551         }*/
51552         
51553         this.updateHeaderSortState();
51554
51555         this.beforeInitialResize();
51556         this.layout(true);
51557
51558         // two part rendering gives faster view to the user
51559         this.renderPhase2.defer(1, this);
51560     },
51561
51562     renderPhase2 : function(){
51563         // render the rows now
51564         this.refresh();
51565         if(this.grid.autoSizeColumns){
51566             this.autoSizeColumns();
51567         }
51568     },
51569
51570     beforeInitialResize : function(){
51571
51572     },
51573
51574     onColumnSplitterMoved : function(i, w){
51575         this.userResized = true;
51576         var cm = this.grid.colModel;
51577         cm.setColumnWidth(i, w, true);
51578         var cid = cm.getColumnId(i);
51579         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
51580         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
51581         this.updateSplitters();
51582         this.layout();
51583         this.grid.fireEvent("columnresize", i, w);
51584     },
51585
51586     syncRowHeights : function(startIndex, endIndex){
51587         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
51588             startIndex = startIndex || 0;
51589             var mrows = this.getBodyTable().rows;
51590             var lrows = this.getLockedTable().rows;
51591             var len = mrows.length-1;
51592             endIndex = Math.min(endIndex || len, len);
51593             for(var i = startIndex; i <= endIndex; i++){
51594                 var m = mrows[i], l = lrows[i];
51595                 var h = Math.max(m.offsetHeight, l.offsetHeight);
51596                 m.style.height = l.style.height = h + "px";
51597             }
51598         }
51599     },
51600
51601     layout : function(initialRender, is2ndPass){
51602         var g = this.grid;
51603         var auto = g.autoHeight;
51604         var scrollOffset = 16;
51605         var c = g.getGridEl(), cm = this.cm,
51606                 expandCol = g.autoExpandColumn,
51607                 gv = this;
51608         //c.beginMeasure();
51609
51610         if(!c.dom.offsetWidth){ // display:none?
51611             if(initialRender){
51612                 this.lockedWrap.show();
51613                 this.mainWrap.show();
51614             }
51615             return;
51616         }
51617
51618         var hasLock = this.cm.isLocked(0);
51619
51620         var tbh = this.headerPanel.getHeight();
51621         var bbh = this.footerPanel.getHeight();
51622
51623         if(auto){
51624             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
51625             var newHeight = ch + c.getBorderWidth("tb");
51626             if(g.maxHeight){
51627                 newHeight = Math.min(g.maxHeight, newHeight);
51628             }
51629             c.setHeight(newHeight);
51630         }
51631
51632         if(g.autoWidth){
51633             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
51634         }
51635
51636         var s = this.scroller;
51637
51638         var csize = c.getSize(true);
51639
51640         this.el.setSize(csize.width, csize.height);
51641
51642         this.headerPanel.setWidth(csize.width);
51643         this.footerPanel.setWidth(csize.width);
51644
51645         var hdHeight = this.mainHd.getHeight();
51646         var vw = csize.width;
51647         var vh = csize.height - (tbh + bbh);
51648
51649         s.setSize(vw, vh);
51650
51651         var bt = this.getBodyTable();
51652         var ltWidth = hasLock ?
51653                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
51654
51655         var scrollHeight = bt.offsetHeight;
51656         var scrollWidth = ltWidth + bt.offsetWidth;
51657         var vscroll = false, hscroll = false;
51658
51659         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
51660
51661         var lw = this.lockedWrap, mw = this.mainWrap;
51662         var lb = this.lockedBody, mb = this.mainBody;
51663
51664         setTimeout(function(){
51665             var t = s.dom.offsetTop;
51666             var w = s.dom.clientWidth,
51667                 h = s.dom.clientHeight;
51668
51669             lw.setTop(t);
51670             lw.setSize(ltWidth, h);
51671
51672             mw.setLeftTop(ltWidth, t);
51673             mw.setSize(w-ltWidth, h);
51674
51675             lb.setHeight(h-hdHeight);
51676             mb.setHeight(h-hdHeight);
51677
51678             if(is2ndPass !== true && !gv.userResized && expandCol){
51679                 // high speed resize without full column calculation
51680                 
51681                 var ci = cm.getIndexById(expandCol);
51682                 if (ci < 0) {
51683                     ci = cm.findColumnIndex(expandCol);
51684                 }
51685                 ci = Math.max(0, ci); // make sure it's got at least the first col.
51686                 var expandId = cm.getColumnId(ci);
51687                 var  tw = cm.getTotalWidth(false);
51688                 var currentWidth = cm.getColumnWidth(ci);
51689                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
51690                 if(currentWidth != cw){
51691                     cm.setColumnWidth(ci, cw, true);
51692                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
51693                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
51694                     gv.updateSplitters();
51695                     gv.layout(false, true);
51696                 }
51697             }
51698
51699             if(initialRender){
51700                 lw.show();
51701                 mw.show();
51702             }
51703             //c.endMeasure();
51704         }, 10);
51705     },
51706
51707     onWindowResize : function(){
51708         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
51709             return;
51710         }
51711         this.layout();
51712     },
51713
51714     appendFooter : function(parentEl){
51715         return null;
51716     },
51717
51718     sortAscText : "Sort Ascending",
51719     sortDescText : "Sort Descending",
51720     lockText : "Lock Column",
51721     unlockText : "Unlock Column",
51722     columnsText : "Columns"
51723 });
51724
51725
51726 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
51727     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
51728     this.proxy.el.addClass('x-grid3-col-dd');
51729 };
51730
51731 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
51732     handleMouseDown : function(e){
51733
51734     },
51735
51736     callHandleMouseDown : function(e){
51737         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
51738     }
51739 });
51740 /*
51741  * Based on:
51742  * Ext JS Library 1.1.1
51743  * Copyright(c) 2006-2007, Ext JS, LLC.
51744  *
51745  * Originally Released Under LGPL - original licence link has changed is not relivant.
51746  *
51747  * Fork - LGPL
51748  * <script type="text/javascript">
51749  */
51750  
51751 // private
51752 // This is a support class used internally by the Grid components
51753 Roo.grid.SplitDragZone = function(grid, hd, hd2){
51754     this.grid = grid;
51755     this.view = grid.getView();
51756     this.proxy = this.view.resizeProxy;
51757     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
51758         "gridSplitters" + this.grid.getGridEl().id, {
51759         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
51760     });
51761     this.setHandleElId(Roo.id(hd));
51762     this.setOuterHandleElId(Roo.id(hd2));
51763     this.scroll = false;
51764 };
51765 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
51766     fly: Roo.Element.fly,
51767
51768     b4StartDrag : function(x, y){
51769         this.view.headersDisabled = true;
51770         this.proxy.setHeight(this.view.mainWrap.getHeight());
51771         var w = this.cm.getColumnWidth(this.cellIndex);
51772         var minw = Math.max(w-this.grid.minColumnWidth, 0);
51773         this.resetConstraints();
51774         this.setXConstraint(minw, 1000);
51775         this.setYConstraint(0, 0);
51776         this.minX = x - minw;
51777         this.maxX = x + 1000;
51778         this.startPos = x;
51779         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
51780     },
51781
51782
51783     handleMouseDown : function(e){
51784         ev = Roo.EventObject.setEvent(e);
51785         var t = this.fly(ev.getTarget());
51786         if(t.hasClass("x-grid-split")){
51787             this.cellIndex = this.view.getCellIndex(t.dom);
51788             this.split = t.dom;
51789             this.cm = this.grid.colModel;
51790             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
51791                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
51792             }
51793         }
51794     },
51795
51796     endDrag : function(e){
51797         this.view.headersDisabled = false;
51798         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
51799         var diff = endX - this.startPos;
51800         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
51801     },
51802
51803     autoOffset : function(){
51804         this.setDelta(0,0);
51805     }
51806 });/*
51807  * Based on:
51808  * Ext JS Library 1.1.1
51809  * Copyright(c) 2006-2007, Ext JS, LLC.
51810  *
51811  * Originally Released Under LGPL - original licence link has changed is not relivant.
51812  *
51813  * Fork - LGPL
51814  * <script type="text/javascript">
51815  */
51816  
51817 // private
51818 // This is a support class used internally by the Grid components
51819 Roo.grid.GridDragZone = function(grid, config){
51820     this.view = grid.getView();
51821     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
51822     if(this.view.lockedBody){
51823         this.setHandleElId(Roo.id(this.view.mainBody.dom));
51824         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
51825     }
51826     this.scroll = false;
51827     this.grid = grid;
51828     this.ddel = document.createElement('div');
51829     this.ddel.className = 'x-grid-dd-wrap';
51830 };
51831
51832 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
51833     ddGroup : "GridDD",
51834
51835     getDragData : function(e){
51836         var t = Roo.lib.Event.getTarget(e);
51837         var rowIndex = this.view.findRowIndex(t);
51838         var sm = this.grid.selModel;
51839             
51840         //Roo.log(rowIndex);
51841         
51842         if (sm.getSelectedCell) {
51843             // cell selection..
51844             if (!sm.getSelectedCell()) {
51845                 return false;
51846             }
51847             if (rowIndex != sm.getSelectedCell()[0]) {
51848                 return false;
51849             }
51850         
51851         }
51852         
51853         if(rowIndex !== false){
51854             
51855             // if editorgrid.. 
51856             
51857             
51858             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
51859                
51860             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
51861               //  
51862             //}
51863             if (e.hasModifier()){
51864                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
51865             }
51866             
51867             Roo.log("getDragData");
51868             
51869             return {
51870                 grid: this.grid,
51871                 ddel: this.ddel,
51872                 rowIndex: rowIndex,
51873                 selections:sm.getSelections ? sm.getSelections() : (
51874                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
51875                 )
51876             };
51877         }
51878         return false;
51879     },
51880
51881     onInitDrag : function(e){
51882         var data = this.dragData;
51883         this.ddel.innerHTML = this.grid.getDragDropText();
51884         this.proxy.update(this.ddel);
51885         // fire start drag?
51886     },
51887
51888     afterRepair : function(){
51889         this.dragging = false;
51890     },
51891
51892     getRepairXY : function(e, data){
51893         return false;
51894     },
51895
51896     onEndDrag : function(data, e){
51897         // fire end drag?
51898     },
51899
51900     onValidDrop : function(dd, e, id){
51901         // fire drag drop?
51902         this.hideProxy();
51903     },
51904
51905     beforeInvalidDrop : function(e, id){
51906
51907     }
51908 });/*
51909  * Based on:
51910  * Ext JS Library 1.1.1
51911  * Copyright(c) 2006-2007, Ext JS, LLC.
51912  *
51913  * Originally Released Under LGPL - original licence link has changed is not relivant.
51914  *
51915  * Fork - LGPL
51916  * <script type="text/javascript">
51917  */
51918  
51919
51920 /**
51921  * @class Roo.grid.ColumnModel
51922  * @extends Roo.util.Observable
51923  * This is the default implementation of a ColumnModel used by the Grid. It defines
51924  * the columns in the grid.
51925  * <br>Usage:<br>
51926  <pre><code>
51927  var colModel = new Roo.grid.ColumnModel([
51928         {header: "Ticker", width: 60, sortable: true, locked: true},
51929         {header: "Company Name", width: 150, sortable: true},
51930         {header: "Market Cap.", width: 100, sortable: true},
51931         {header: "$ Sales", width: 100, sortable: true, renderer: money},
51932         {header: "Employees", width: 100, sortable: true, resizable: false}
51933  ]);
51934  </code></pre>
51935  * <p>
51936  
51937  * The config options listed for this class are options which may appear in each
51938  * individual column definition.
51939  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
51940  * @constructor
51941  * @param {Object} config An Array of column config objects. See this class's
51942  * config objects for details.
51943 */
51944 Roo.grid.ColumnModel = function(config){
51945         /**
51946      * The config passed into the constructor
51947      */
51948     this.config = config;
51949     this.lookup = {};
51950
51951     // if no id, create one
51952     // if the column does not have a dataIndex mapping,
51953     // map it to the order it is in the config
51954     for(var i = 0, len = config.length; i < len; i++){
51955         var c = config[i];
51956         if(typeof c.dataIndex == "undefined"){
51957             c.dataIndex = i;
51958         }
51959         if(typeof c.renderer == "string"){
51960             c.renderer = Roo.util.Format[c.renderer];
51961         }
51962         if(typeof c.id == "undefined"){
51963             c.id = Roo.id();
51964         }
51965         if(c.editor && c.editor.xtype){
51966             c.editor  = Roo.factory(c.editor, Roo.grid);
51967         }
51968         if(c.editor && c.editor.isFormField){
51969             c.editor = new Roo.grid.GridEditor(c.editor);
51970         }
51971         this.lookup[c.id] = c;
51972     }
51973
51974     /**
51975      * The width of columns which have no width specified (defaults to 100)
51976      * @type Number
51977      */
51978     this.defaultWidth = 100;
51979
51980     /**
51981      * Default sortable of columns which have no sortable specified (defaults to false)
51982      * @type Boolean
51983      */
51984     this.defaultSortable = false;
51985
51986     this.addEvents({
51987         /**
51988              * @event widthchange
51989              * Fires when the width of a column changes.
51990              * @param {ColumnModel} this
51991              * @param {Number} columnIndex The column index
51992              * @param {Number} newWidth The new width
51993              */
51994             "widthchange": true,
51995         /**
51996              * @event headerchange
51997              * Fires when the text of a header changes.
51998              * @param {ColumnModel} this
51999              * @param {Number} columnIndex The column index
52000              * @param {Number} newText The new header text
52001              */
52002             "headerchange": true,
52003         /**
52004              * @event hiddenchange
52005              * Fires when a column is hidden or "unhidden".
52006              * @param {ColumnModel} this
52007              * @param {Number} columnIndex The column index
52008              * @param {Boolean} hidden true if hidden, false otherwise
52009              */
52010             "hiddenchange": true,
52011             /**
52012          * @event columnmoved
52013          * Fires when a column is moved.
52014          * @param {ColumnModel} this
52015          * @param {Number} oldIndex
52016          * @param {Number} newIndex
52017          */
52018         "columnmoved" : true,
52019         /**
52020          * @event columlockchange
52021          * Fires when a column's locked state is changed
52022          * @param {ColumnModel} this
52023          * @param {Number} colIndex
52024          * @param {Boolean} locked true if locked
52025          */
52026         "columnlockchange" : true
52027     });
52028     Roo.grid.ColumnModel.superclass.constructor.call(this);
52029 };
52030 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
52031     /**
52032      * @cfg {String} header The header text to display in the Grid view.
52033      */
52034     /**
52035      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
52036      * {@link Roo.data.Record} definition from which to draw the column's value. If not
52037      * specified, the column's index is used as an index into the Record's data Array.
52038      */
52039     /**
52040      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
52041      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
52042      */
52043     /**
52044      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
52045      * Defaults to the value of the {@link #defaultSortable} property.
52046      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
52047      */
52048     /**
52049      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
52050      */
52051     /**
52052      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
52053      */
52054     /**
52055      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
52056      */
52057     /**
52058      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
52059      */
52060     /**
52061      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
52062      * given the cell's data value. See {@link #setRenderer}. If not specified, the
52063      * default renderer uses the raw data value.
52064      */
52065        /**
52066      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
52067      */
52068     /**
52069      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
52070      */
52071
52072     /**
52073      * Returns the id of the column at the specified index.
52074      * @param {Number} index The column index
52075      * @return {String} the id
52076      */
52077     getColumnId : function(index){
52078         return this.config[index].id;
52079     },
52080
52081     /**
52082      * Returns the column for a specified id.
52083      * @param {String} id The column id
52084      * @return {Object} the column
52085      */
52086     getColumnById : function(id){
52087         return this.lookup[id];
52088     },
52089
52090     
52091     /**
52092      * Returns the column for a specified dataIndex.
52093      * @param {String} dataIndex The column dataIndex
52094      * @return {Object|Boolean} the column or false if not found
52095      */
52096     getColumnByDataIndex: function(dataIndex){
52097         var index = this.findColumnIndex(dataIndex);
52098         return index > -1 ? this.config[index] : false;
52099     },
52100     
52101     /**
52102      * Returns the index for a specified column id.
52103      * @param {String} id The column id
52104      * @return {Number} the index, or -1 if not found
52105      */
52106     getIndexById : function(id){
52107         for(var i = 0, len = this.config.length; i < len; i++){
52108             if(this.config[i].id == id){
52109                 return i;
52110             }
52111         }
52112         return -1;
52113     },
52114     
52115     /**
52116      * Returns the index for a specified column dataIndex.
52117      * @param {String} dataIndex The column dataIndex
52118      * @return {Number} the index, or -1 if not found
52119      */
52120     
52121     findColumnIndex : function(dataIndex){
52122         for(var i = 0, len = this.config.length; i < len; i++){
52123             if(this.config[i].dataIndex == dataIndex){
52124                 return i;
52125             }
52126         }
52127         return -1;
52128     },
52129     
52130     
52131     moveColumn : function(oldIndex, newIndex){
52132         var c = this.config[oldIndex];
52133         this.config.splice(oldIndex, 1);
52134         this.config.splice(newIndex, 0, c);
52135         this.dataMap = null;
52136         this.fireEvent("columnmoved", this, oldIndex, newIndex);
52137     },
52138
52139     isLocked : function(colIndex){
52140         return this.config[colIndex].locked === true;
52141     },
52142
52143     setLocked : function(colIndex, value, suppressEvent){
52144         if(this.isLocked(colIndex) == value){
52145             return;
52146         }
52147         this.config[colIndex].locked = value;
52148         if(!suppressEvent){
52149             this.fireEvent("columnlockchange", this, colIndex, value);
52150         }
52151     },
52152
52153     getTotalLockedWidth : function(){
52154         var totalWidth = 0;
52155         for(var i = 0; i < this.config.length; i++){
52156             if(this.isLocked(i) && !this.isHidden(i)){
52157                 this.totalWidth += this.getColumnWidth(i);
52158             }
52159         }
52160         return totalWidth;
52161     },
52162
52163     getLockedCount : function(){
52164         for(var i = 0, len = this.config.length; i < len; i++){
52165             if(!this.isLocked(i)){
52166                 return i;
52167             }
52168         }
52169     },
52170
52171     /**
52172      * Returns the number of columns.
52173      * @return {Number}
52174      */
52175     getColumnCount : function(visibleOnly){
52176         if(visibleOnly === true){
52177             var c = 0;
52178             for(var i = 0, len = this.config.length; i < len; i++){
52179                 if(!this.isHidden(i)){
52180                     c++;
52181                 }
52182             }
52183             return c;
52184         }
52185         return this.config.length;
52186     },
52187
52188     /**
52189      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
52190      * @param {Function} fn
52191      * @param {Object} scope (optional)
52192      * @return {Array} result
52193      */
52194     getColumnsBy : function(fn, scope){
52195         var r = [];
52196         for(var i = 0, len = this.config.length; i < len; i++){
52197             var c = this.config[i];
52198             if(fn.call(scope||this, c, i) === true){
52199                 r[r.length] = c;
52200             }
52201         }
52202         return r;
52203     },
52204
52205     /**
52206      * Returns true if the specified column is sortable.
52207      * @param {Number} col The column index
52208      * @return {Boolean}
52209      */
52210     isSortable : function(col){
52211         if(typeof this.config[col].sortable == "undefined"){
52212             return this.defaultSortable;
52213         }
52214         return this.config[col].sortable;
52215     },
52216
52217     /**
52218      * Returns the rendering (formatting) function defined for the column.
52219      * @param {Number} col The column index.
52220      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
52221      */
52222     getRenderer : function(col){
52223         if(!this.config[col].renderer){
52224             return Roo.grid.ColumnModel.defaultRenderer;
52225         }
52226         return this.config[col].renderer;
52227     },
52228
52229     /**
52230      * Sets the rendering (formatting) function for a column.
52231      * @param {Number} col The column index
52232      * @param {Function} fn The function to use to process the cell's raw data
52233      * to return HTML markup for the grid view. The render function is called with
52234      * the following parameters:<ul>
52235      * <li>Data value.</li>
52236      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
52237      * <li>css A CSS style string to apply to the table cell.</li>
52238      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
52239      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
52240      * <li>Row index</li>
52241      * <li>Column index</li>
52242      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
52243      */
52244     setRenderer : function(col, fn){
52245         this.config[col].renderer = fn;
52246     },
52247
52248     /**
52249      * Returns the width for the specified column.
52250      * @param {Number} col The column index
52251      * @return {Number}
52252      */
52253     getColumnWidth : function(col){
52254         return this.config[col].width * 1 || this.defaultWidth;
52255     },
52256
52257     /**
52258      * Sets the width for a column.
52259      * @param {Number} col The column index
52260      * @param {Number} width The new width
52261      */
52262     setColumnWidth : function(col, width, suppressEvent){
52263         this.config[col].width = width;
52264         this.totalWidth = null;
52265         if(!suppressEvent){
52266              this.fireEvent("widthchange", this, col, width);
52267         }
52268     },
52269
52270     /**
52271      * Returns the total width of all columns.
52272      * @param {Boolean} includeHidden True to include hidden column widths
52273      * @return {Number}
52274      */
52275     getTotalWidth : function(includeHidden){
52276         if(!this.totalWidth){
52277             this.totalWidth = 0;
52278             for(var i = 0, len = this.config.length; i < len; i++){
52279                 if(includeHidden || !this.isHidden(i)){
52280                     this.totalWidth += this.getColumnWidth(i);
52281                 }
52282             }
52283         }
52284         return this.totalWidth;
52285     },
52286
52287     /**
52288      * Returns the header for the specified column.
52289      * @param {Number} col The column index
52290      * @return {String}
52291      */
52292     getColumnHeader : function(col){
52293         return this.config[col].header;
52294     },
52295
52296     /**
52297      * Sets the header for a column.
52298      * @param {Number} col The column index
52299      * @param {String} header The new header
52300      */
52301     setColumnHeader : function(col, header){
52302         this.config[col].header = header;
52303         this.fireEvent("headerchange", this, col, header);
52304     },
52305
52306     /**
52307      * Returns the tooltip for the specified column.
52308      * @param {Number} col The column index
52309      * @return {String}
52310      */
52311     getColumnTooltip : function(col){
52312             return this.config[col].tooltip;
52313     },
52314     /**
52315      * Sets the tooltip for a column.
52316      * @param {Number} col The column index
52317      * @param {String} tooltip The new tooltip
52318      */
52319     setColumnTooltip : function(col, tooltip){
52320             this.config[col].tooltip = tooltip;
52321     },
52322
52323     /**
52324      * Returns the dataIndex for the specified column.
52325      * @param {Number} col The column index
52326      * @return {Number}
52327      */
52328     getDataIndex : function(col){
52329         return this.config[col].dataIndex;
52330     },
52331
52332     /**
52333      * Sets the dataIndex for a column.
52334      * @param {Number} col The column index
52335      * @param {Number} dataIndex The new dataIndex
52336      */
52337     setDataIndex : function(col, dataIndex){
52338         this.config[col].dataIndex = dataIndex;
52339     },
52340
52341     
52342     
52343     /**
52344      * Returns true if the cell is editable.
52345      * @param {Number} colIndex The column index
52346      * @param {Number} rowIndex The row index
52347      * @return {Boolean}
52348      */
52349     isCellEditable : function(colIndex, rowIndex){
52350         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
52351     },
52352
52353     /**
52354      * Returns the editor defined for the cell/column.
52355      * return false or null to disable editing.
52356      * @param {Number} colIndex The column index
52357      * @param {Number} rowIndex The row index
52358      * @return {Object}
52359      */
52360     getCellEditor : function(colIndex, rowIndex){
52361         return this.config[colIndex].editor;
52362     },
52363
52364     /**
52365      * Sets if a column is editable.
52366      * @param {Number} col The column index
52367      * @param {Boolean} editable True if the column is editable
52368      */
52369     setEditable : function(col, editable){
52370         this.config[col].editable = editable;
52371     },
52372
52373
52374     /**
52375      * Returns true if the column is hidden.
52376      * @param {Number} colIndex The column index
52377      * @return {Boolean}
52378      */
52379     isHidden : function(colIndex){
52380         return this.config[colIndex].hidden;
52381     },
52382
52383
52384     /**
52385      * Returns true if the column width cannot be changed
52386      */
52387     isFixed : function(colIndex){
52388         return this.config[colIndex].fixed;
52389     },
52390
52391     /**
52392      * Returns true if the column can be resized
52393      * @return {Boolean}
52394      */
52395     isResizable : function(colIndex){
52396         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
52397     },
52398     /**
52399      * Sets if a column is hidden.
52400      * @param {Number} colIndex The column index
52401      * @param {Boolean} hidden True if the column is hidden
52402      */
52403     setHidden : function(colIndex, hidden){
52404         this.config[colIndex].hidden = hidden;
52405         this.totalWidth = null;
52406         this.fireEvent("hiddenchange", this, colIndex, hidden);
52407     },
52408
52409     /**
52410      * Sets the editor for a column.
52411      * @param {Number} col The column index
52412      * @param {Object} editor The editor object
52413      */
52414     setEditor : function(col, editor){
52415         this.config[col].editor = editor;
52416     }
52417 });
52418
52419 Roo.grid.ColumnModel.defaultRenderer = function(value){
52420         if(typeof value == "string" && value.length < 1){
52421             return "&#160;";
52422         }
52423         return value;
52424 };
52425
52426 // Alias for backwards compatibility
52427 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
52428 /*
52429  * Based on:
52430  * Ext JS Library 1.1.1
52431  * Copyright(c) 2006-2007, Ext JS, LLC.
52432  *
52433  * Originally Released Under LGPL - original licence link has changed is not relivant.
52434  *
52435  * Fork - LGPL
52436  * <script type="text/javascript">
52437  */
52438
52439 /**
52440  * @class Roo.grid.AbstractSelectionModel
52441  * @extends Roo.util.Observable
52442  * Abstract base class for grid SelectionModels.  It provides the interface that should be
52443  * implemented by descendant classes.  This class should not be directly instantiated.
52444  * @constructor
52445  */
52446 Roo.grid.AbstractSelectionModel = function(){
52447     this.locked = false;
52448     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
52449 };
52450
52451 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
52452     /** @ignore Called by the grid automatically. Do not call directly. */
52453     init : function(grid){
52454         this.grid = grid;
52455         this.initEvents();
52456     },
52457
52458     /**
52459      * Locks the selections.
52460      */
52461     lock : function(){
52462         this.locked = true;
52463     },
52464
52465     /**
52466      * Unlocks the selections.
52467      */
52468     unlock : function(){
52469         this.locked = false;
52470     },
52471
52472     /**
52473      * Returns true if the selections are locked.
52474      * @return {Boolean}
52475      */
52476     isLocked : function(){
52477         return this.locked;
52478     }
52479 });/*
52480  * Based on:
52481  * Ext JS Library 1.1.1
52482  * Copyright(c) 2006-2007, Ext JS, LLC.
52483  *
52484  * Originally Released Under LGPL - original licence link has changed is not relivant.
52485  *
52486  * Fork - LGPL
52487  * <script type="text/javascript">
52488  */
52489 /**
52490  * @extends Roo.grid.AbstractSelectionModel
52491  * @class Roo.grid.RowSelectionModel
52492  * The default SelectionModel used by {@link Roo.grid.Grid}.
52493  * It supports multiple selections and keyboard selection/navigation. 
52494  * @constructor
52495  * @param {Object} config
52496  */
52497 Roo.grid.RowSelectionModel = function(config){
52498     Roo.apply(this, config);
52499     this.selections = new Roo.util.MixedCollection(false, function(o){
52500         return o.id;
52501     });
52502
52503     this.last = false;
52504     this.lastActive = false;
52505
52506     this.addEvents({
52507         /**
52508              * @event selectionchange
52509              * Fires when the selection changes
52510              * @param {SelectionModel} this
52511              */
52512             "selectionchange" : true,
52513         /**
52514              * @event afterselectionchange
52515              * Fires after the selection changes (eg. by key press or clicking)
52516              * @param {SelectionModel} this
52517              */
52518             "afterselectionchange" : true,
52519         /**
52520              * @event beforerowselect
52521              * Fires when a row is selected being selected, return false to cancel.
52522              * @param {SelectionModel} this
52523              * @param {Number} rowIndex The selected index
52524              * @param {Boolean} keepExisting False if other selections will be cleared
52525              */
52526             "beforerowselect" : true,
52527         /**
52528              * @event rowselect
52529              * Fires when a row is selected.
52530              * @param {SelectionModel} this
52531              * @param {Number} rowIndex The selected index
52532              * @param {Roo.data.Record} r The record
52533              */
52534             "rowselect" : true,
52535         /**
52536              * @event rowdeselect
52537              * Fires when a row is deselected.
52538              * @param {SelectionModel} this
52539              * @param {Number} rowIndex The selected index
52540              */
52541         "rowdeselect" : true
52542     });
52543     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
52544     this.locked = false;
52545 };
52546
52547 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
52548     /**
52549      * @cfg {Boolean} singleSelect
52550      * True to allow selection of only one row at a time (defaults to false)
52551      */
52552     singleSelect : false,
52553
52554     // private
52555     initEvents : function(){
52556
52557         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
52558             this.grid.on("mousedown", this.handleMouseDown, this);
52559         }else{ // allow click to work like normal
52560             this.grid.on("rowclick", this.handleDragableRowClick, this);
52561         }
52562
52563         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
52564             "up" : function(e){
52565                 if(!e.shiftKey){
52566                     this.selectPrevious(e.shiftKey);
52567                 }else if(this.last !== false && this.lastActive !== false){
52568                     var last = this.last;
52569                     this.selectRange(this.last,  this.lastActive-1);
52570                     this.grid.getView().focusRow(this.lastActive);
52571                     if(last !== false){
52572                         this.last = last;
52573                     }
52574                 }else{
52575                     this.selectFirstRow();
52576                 }
52577                 this.fireEvent("afterselectionchange", this);
52578             },
52579             "down" : function(e){
52580                 if(!e.shiftKey){
52581                     this.selectNext(e.shiftKey);
52582                 }else if(this.last !== false && this.lastActive !== false){
52583                     var last = this.last;
52584                     this.selectRange(this.last,  this.lastActive+1);
52585                     this.grid.getView().focusRow(this.lastActive);
52586                     if(last !== false){
52587                         this.last = last;
52588                     }
52589                 }else{
52590                     this.selectFirstRow();
52591                 }
52592                 this.fireEvent("afterselectionchange", this);
52593             },
52594             scope: this
52595         });
52596
52597         var view = this.grid.view;
52598         view.on("refresh", this.onRefresh, this);
52599         view.on("rowupdated", this.onRowUpdated, this);
52600         view.on("rowremoved", this.onRemove, this);
52601     },
52602
52603     // private
52604     onRefresh : function(){
52605         var ds = this.grid.dataSource, i, v = this.grid.view;
52606         var s = this.selections;
52607         s.each(function(r){
52608             if((i = ds.indexOfId(r.id)) != -1){
52609                 v.onRowSelect(i);
52610             }else{
52611                 s.remove(r);
52612             }
52613         });
52614     },
52615
52616     // private
52617     onRemove : function(v, index, r){
52618         this.selections.remove(r);
52619     },
52620
52621     // private
52622     onRowUpdated : function(v, index, r){
52623         if(this.isSelected(r)){
52624             v.onRowSelect(index);
52625         }
52626     },
52627
52628     /**
52629      * Select records.
52630      * @param {Array} records The records to select
52631      * @param {Boolean} keepExisting (optional) True to keep existing selections
52632      */
52633     selectRecords : function(records, keepExisting){
52634         if(!keepExisting){
52635             this.clearSelections();
52636         }
52637         var ds = this.grid.dataSource;
52638         for(var i = 0, len = records.length; i < len; i++){
52639             this.selectRow(ds.indexOf(records[i]), true);
52640         }
52641     },
52642
52643     /**
52644      * Gets the number of selected rows.
52645      * @return {Number}
52646      */
52647     getCount : function(){
52648         return this.selections.length;
52649     },
52650
52651     /**
52652      * Selects the first row in the grid.
52653      */
52654     selectFirstRow : function(){
52655         this.selectRow(0);
52656     },
52657
52658     /**
52659      * Select the last row.
52660      * @param {Boolean} keepExisting (optional) True to keep existing selections
52661      */
52662     selectLastRow : function(keepExisting){
52663         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
52664     },
52665
52666     /**
52667      * Selects the row immediately following the last selected row.
52668      * @param {Boolean} keepExisting (optional) True to keep existing selections
52669      */
52670     selectNext : function(keepExisting){
52671         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
52672             this.selectRow(this.last+1, keepExisting);
52673             this.grid.getView().focusRow(this.last);
52674         }
52675     },
52676
52677     /**
52678      * Selects the row that precedes the last selected row.
52679      * @param {Boolean} keepExisting (optional) True to keep existing selections
52680      */
52681     selectPrevious : function(keepExisting){
52682         if(this.last){
52683             this.selectRow(this.last-1, keepExisting);
52684             this.grid.getView().focusRow(this.last);
52685         }
52686     },
52687
52688     /**
52689      * Returns the selected records
52690      * @return {Array} Array of selected records
52691      */
52692     getSelections : function(){
52693         return [].concat(this.selections.items);
52694     },
52695
52696     /**
52697      * Returns the first selected record.
52698      * @return {Record}
52699      */
52700     getSelected : function(){
52701         return this.selections.itemAt(0);
52702     },
52703
52704
52705     /**
52706      * Clears all selections.
52707      */
52708     clearSelections : function(fast){
52709         if(this.locked) return;
52710         if(fast !== true){
52711             var ds = this.grid.dataSource;
52712             var s = this.selections;
52713             s.each(function(r){
52714                 this.deselectRow(ds.indexOfId(r.id));
52715             }, this);
52716             s.clear();
52717         }else{
52718             this.selections.clear();
52719         }
52720         this.last = false;
52721     },
52722
52723
52724     /**
52725      * Selects all rows.
52726      */
52727     selectAll : function(){
52728         if(this.locked) return;
52729         this.selections.clear();
52730         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
52731             this.selectRow(i, true);
52732         }
52733     },
52734
52735     /**
52736      * Returns True if there is a selection.
52737      * @return {Boolean}
52738      */
52739     hasSelection : function(){
52740         return this.selections.length > 0;
52741     },
52742
52743     /**
52744      * Returns True if the specified row is selected.
52745      * @param {Number/Record} record The record or index of the record to check
52746      * @return {Boolean}
52747      */
52748     isSelected : function(index){
52749         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
52750         return (r && this.selections.key(r.id) ? true : false);
52751     },
52752
52753     /**
52754      * Returns True if the specified record id is selected.
52755      * @param {String} id The id of record to check
52756      * @return {Boolean}
52757      */
52758     isIdSelected : function(id){
52759         return (this.selections.key(id) ? true : false);
52760     },
52761
52762     // private
52763     handleMouseDown : function(e, t){
52764         var view = this.grid.getView(), rowIndex;
52765         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
52766             return;
52767         };
52768         if(e.shiftKey && this.last !== false){
52769             var last = this.last;
52770             this.selectRange(last, rowIndex, e.ctrlKey);
52771             this.last = last; // reset the last
52772             view.focusRow(rowIndex);
52773         }else{
52774             var isSelected = this.isSelected(rowIndex);
52775             if(e.button !== 0 && isSelected){
52776                 view.focusRow(rowIndex);
52777             }else if(e.ctrlKey && isSelected){
52778                 this.deselectRow(rowIndex);
52779             }else if(!isSelected){
52780                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
52781                 view.focusRow(rowIndex);
52782             }
52783         }
52784         this.fireEvent("afterselectionchange", this);
52785     },
52786     // private
52787     handleDragableRowClick :  function(grid, rowIndex, e) 
52788     {
52789         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
52790             this.selectRow(rowIndex, false);
52791             grid.view.focusRow(rowIndex);
52792              this.fireEvent("afterselectionchange", this);
52793         }
52794     },
52795     
52796     /**
52797      * Selects multiple rows.
52798      * @param {Array} rows Array of the indexes of the row to select
52799      * @param {Boolean} keepExisting (optional) True to keep existing selections
52800      */
52801     selectRows : function(rows, keepExisting){
52802         if(!keepExisting){
52803             this.clearSelections();
52804         }
52805         for(var i = 0, len = rows.length; i < len; i++){
52806             this.selectRow(rows[i], true);
52807         }
52808     },
52809
52810     /**
52811      * Selects a range of rows. All rows in between startRow and endRow are also selected.
52812      * @param {Number} startRow The index of the first row in the range
52813      * @param {Number} endRow The index of the last row in the range
52814      * @param {Boolean} keepExisting (optional) True to retain existing selections
52815      */
52816     selectRange : function(startRow, endRow, keepExisting){
52817         if(this.locked) return;
52818         if(!keepExisting){
52819             this.clearSelections();
52820         }
52821         if(startRow <= endRow){
52822             for(var i = startRow; i <= endRow; i++){
52823                 this.selectRow(i, true);
52824             }
52825         }else{
52826             for(var i = startRow; i >= endRow; i--){
52827                 this.selectRow(i, true);
52828             }
52829         }
52830     },
52831
52832     /**
52833      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
52834      * @param {Number} startRow The index of the first row in the range
52835      * @param {Number} endRow The index of the last row in the range
52836      */
52837     deselectRange : function(startRow, endRow, preventViewNotify){
52838         if(this.locked) return;
52839         for(var i = startRow; i <= endRow; i++){
52840             this.deselectRow(i, preventViewNotify);
52841         }
52842     },
52843
52844     /**
52845      * Selects a row.
52846      * @param {Number} row The index of the row to select
52847      * @param {Boolean} keepExisting (optional) True to keep existing selections
52848      */
52849     selectRow : function(index, keepExisting, preventViewNotify){
52850         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
52851         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
52852             if(!keepExisting || this.singleSelect){
52853                 this.clearSelections();
52854             }
52855             var r = this.grid.dataSource.getAt(index);
52856             this.selections.add(r);
52857             this.last = this.lastActive = index;
52858             if(!preventViewNotify){
52859                 this.grid.getView().onRowSelect(index);
52860             }
52861             this.fireEvent("rowselect", this, index, r);
52862             this.fireEvent("selectionchange", this);
52863         }
52864     },
52865
52866     /**
52867      * Deselects a row.
52868      * @param {Number} row The index of the row to deselect
52869      */
52870     deselectRow : function(index, preventViewNotify){
52871         if(this.locked) return;
52872         if(this.last == index){
52873             this.last = false;
52874         }
52875         if(this.lastActive == index){
52876             this.lastActive = false;
52877         }
52878         var r = this.grid.dataSource.getAt(index);
52879         this.selections.remove(r);
52880         if(!preventViewNotify){
52881             this.grid.getView().onRowDeselect(index);
52882         }
52883         this.fireEvent("rowdeselect", this, index);
52884         this.fireEvent("selectionchange", this);
52885     },
52886
52887     // private
52888     restoreLast : function(){
52889         if(this._last){
52890             this.last = this._last;
52891         }
52892     },
52893
52894     // private
52895     acceptsNav : function(row, col, cm){
52896         return !cm.isHidden(col) && cm.isCellEditable(col, row);
52897     },
52898
52899     // private
52900     onEditorKey : function(field, e){
52901         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
52902         if(k == e.TAB){
52903             e.stopEvent();
52904             ed.completeEdit();
52905             if(e.shiftKey){
52906                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
52907             }else{
52908                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
52909             }
52910         }else if(k == e.ENTER && !e.ctrlKey){
52911             e.stopEvent();
52912             ed.completeEdit();
52913             if(e.shiftKey){
52914                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
52915             }else{
52916                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
52917             }
52918         }else if(k == e.ESC){
52919             ed.cancelEdit();
52920         }
52921         if(newCell){
52922             g.startEditing(newCell[0], newCell[1]);
52923         }
52924     }
52925 });/*
52926  * Based on:
52927  * Ext JS Library 1.1.1
52928  * Copyright(c) 2006-2007, Ext JS, LLC.
52929  *
52930  * Originally Released Under LGPL - original licence link has changed is not relivant.
52931  *
52932  * Fork - LGPL
52933  * <script type="text/javascript">
52934  */
52935 /**
52936  * @class Roo.grid.CellSelectionModel
52937  * @extends Roo.grid.AbstractSelectionModel
52938  * This class provides the basic implementation for cell selection in a grid.
52939  * @constructor
52940  * @param {Object} config The object containing the configuration of this model.
52941  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
52942  */
52943 Roo.grid.CellSelectionModel = function(config){
52944     Roo.apply(this, config);
52945
52946     this.selection = null;
52947
52948     this.addEvents({
52949         /**
52950              * @event beforerowselect
52951              * Fires before a cell is selected.
52952              * @param {SelectionModel} this
52953              * @param {Number} rowIndex The selected row index
52954              * @param {Number} colIndex The selected cell index
52955              */
52956             "beforecellselect" : true,
52957         /**
52958              * @event cellselect
52959              * Fires when a cell is selected.
52960              * @param {SelectionModel} this
52961              * @param {Number} rowIndex The selected row index
52962              * @param {Number} colIndex The selected cell index
52963              */
52964             "cellselect" : true,
52965         /**
52966              * @event selectionchange
52967              * Fires when the active selection changes.
52968              * @param {SelectionModel} this
52969              * @param {Object} selection null for no selection or an object (o) with two properties
52970                 <ul>
52971                 <li>o.record: the record object for the row the selection is in</li>
52972                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
52973                 </ul>
52974              */
52975             "selectionchange" : true,
52976         /**
52977              * @event tabend
52978              * Fires when the tab (or enter) was pressed on the last editable cell
52979              * You can use this to trigger add new row.
52980              * @param {SelectionModel} this
52981              */
52982             "tabend" : true,
52983          /**
52984              * @event beforeeditnext
52985              * Fires before the next editable sell is made active
52986              * You can use this to skip to another cell or fire the tabend
52987              *    if you set cell to false
52988              * @param {Object} eventdata object : { cell : [ row, col ] } 
52989              */
52990             "beforeeditnext" : true
52991     });
52992     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
52993 };
52994
52995 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
52996     
52997     enter_is_tab: false,
52998
52999     /** @ignore */
53000     initEvents : function(){
53001         this.grid.on("mousedown", this.handleMouseDown, this);
53002         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
53003         var view = this.grid.view;
53004         view.on("refresh", this.onViewChange, this);
53005         view.on("rowupdated", this.onRowUpdated, this);
53006         view.on("beforerowremoved", this.clearSelections, this);
53007         view.on("beforerowsinserted", this.clearSelections, this);
53008         if(this.grid.isEditor){
53009             this.grid.on("beforeedit", this.beforeEdit,  this);
53010         }
53011     },
53012
53013         //private
53014     beforeEdit : function(e){
53015         this.select(e.row, e.column, false, true, e.record);
53016     },
53017
53018         //private
53019     onRowUpdated : function(v, index, r){
53020         if(this.selection && this.selection.record == r){
53021             v.onCellSelect(index, this.selection.cell[1]);
53022         }
53023     },
53024
53025         //private
53026     onViewChange : function(){
53027         this.clearSelections(true);
53028     },
53029
53030         /**
53031          * Returns the currently selected cell,.
53032          * @return {Array} The selected cell (row, column) or null if none selected.
53033          */
53034     getSelectedCell : function(){
53035         return this.selection ? this.selection.cell : null;
53036     },
53037
53038     /**
53039      * Clears all selections.
53040      * @param {Boolean} true to prevent the gridview from being notified about the change.
53041      */
53042     clearSelections : function(preventNotify){
53043         var s = this.selection;
53044         if(s){
53045             if(preventNotify !== true){
53046                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
53047             }
53048             this.selection = null;
53049             this.fireEvent("selectionchange", this, null);
53050         }
53051     },
53052
53053     /**
53054      * Returns true if there is a selection.
53055      * @return {Boolean}
53056      */
53057     hasSelection : function(){
53058         return this.selection ? true : false;
53059     },
53060
53061     /** @ignore */
53062     handleMouseDown : function(e, t){
53063         var v = this.grid.getView();
53064         if(this.isLocked()){
53065             return;
53066         };
53067         var row = v.findRowIndex(t);
53068         var cell = v.findCellIndex(t);
53069         if(row !== false && cell !== false){
53070             this.select(row, cell);
53071         }
53072     },
53073
53074     /**
53075      * Selects a cell.
53076      * @param {Number} rowIndex
53077      * @param {Number} collIndex
53078      */
53079     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
53080         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
53081             this.clearSelections();
53082             r = r || this.grid.dataSource.getAt(rowIndex);
53083             this.selection = {
53084                 record : r,
53085                 cell : [rowIndex, colIndex]
53086             };
53087             if(!preventViewNotify){
53088                 var v = this.grid.getView();
53089                 v.onCellSelect(rowIndex, colIndex);
53090                 if(preventFocus !== true){
53091                     v.focusCell(rowIndex, colIndex);
53092                 }
53093             }
53094             this.fireEvent("cellselect", this, rowIndex, colIndex);
53095             this.fireEvent("selectionchange", this, this.selection);
53096         }
53097     },
53098
53099         //private
53100     isSelectable : function(rowIndex, colIndex, cm){
53101         return !cm.isHidden(colIndex);
53102     },
53103
53104     /** @ignore */
53105     handleKeyDown : function(e){
53106         //Roo.log('Cell Sel Model handleKeyDown');
53107         if(!e.isNavKeyPress()){
53108             return;
53109         }
53110         var g = this.grid, s = this.selection;
53111         if(!s){
53112             e.stopEvent();
53113             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
53114             if(cell){
53115                 this.select(cell[0], cell[1]);
53116             }
53117             return;
53118         }
53119         var sm = this;
53120         var walk = function(row, col, step){
53121             return g.walkCells(row, col, step, sm.isSelectable,  sm);
53122         };
53123         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
53124         var newCell;
53125
53126       
53127
53128         switch(k){
53129             case e.TAB:
53130                 // handled by onEditorKey
53131                 if (g.isEditor && g.editing) {
53132                     return;
53133                 }
53134                 if(e.shiftKey) {
53135                     newCell = walk(r, c-1, -1);
53136                 } else {
53137                     newCell = walk(r, c+1, 1);
53138                 }
53139                 break;
53140             
53141             case e.DOWN:
53142                newCell = walk(r+1, c, 1);
53143                 break;
53144             
53145             case e.UP:
53146                 newCell = walk(r-1, c, -1);
53147                 break;
53148             
53149             case e.RIGHT:
53150                 newCell = walk(r, c+1, 1);
53151                 break;
53152             
53153             case e.LEFT:
53154                 newCell = walk(r, c-1, -1);
53155                 break;
53156             
53157             case e.ENTER:
53158                 
53159                 if(g.isEditor && !g.editing){
53160                    g.startEditing(r, c);
53161                    e.stopEvent();
53162                    return;
53163                 }
53164                 
53165                 
53166              break;
53167         };
53168         if(newCell){
53169             this.select(newCell[0], newCell[1]);
53170             e.stopEvent();
53171             
53172         }
53173     },
53174
53175     acceptsNav : function(row, col, cm){
53176         return !cm.isHidden(col) && cm.isCellEditable(col, row);
53177     },
53178     /**
53179      * Selects a cell.
53180      * @param {Number} field (not used) - as it's normally used as a listener
53181      * @param {Number} e - event - fake it by using
53182      *
53183      * var e = Roo.EventObjectImpl.prototype;
53184      * e.keyCode = e.TAB
53185      *
53186      * 
53187      */
53188     onEditorKey : function(field, e){
53189         
53190         var k = e.getKey(),
53191             newCell,
53192             g = this.grid,
53193             ed = g.activeEditor,
53194             forward = false;
53195         ///Roo.log('onEditorKey' + k);
53196         
53197         
53198         if (this.enter_is_tab && k == e.ENTER) {
53199             k = e.TAB;
53200         }
53201         
53202         if(k == e.TAB){
53203             if(e.shiftKey){
53204                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
53205             }else{
53206                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53207                 forward = true;
53208             }
53209             
53210             e.stopEvent();
53211             
53212         } else if(k == e.ENTER &&  !e.ctrlKey){
53213             ed.completeEdit();
53214             e.stopEvent();
53215             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53216         
53217                 } else if(k == e.ESC){
53218             ed.cancelEdit();
53219         }
53220                 
53221         if (newCell) {
53222             var ecall = { cell : newCell, forward : forward };
53223             this.fireEvent('beforeeditnext', ecall );
53224             newCell = ecall.cell;
53225                         forward = ecall.forward;
53226         }
53227                 
53228         if(newCell){
53229             //Roo.log('next cell after edit');
53230             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
53231         } else if (forward) {
53232             // tabbed past last
53233             this.fireEvent.defer(100, this, ['tabend',this]);
53234         }
53235     }
53236 });/*
53237  * Based on:
53238  * Ext JS Library 1.1.1
53239  * Copyright(c) 2006-2007, Ext JS, LLC.
53240  *
53241  * Originally Released Under LGPL - original licence link has changed is not relivant.
53242  *
53243  * Fork - LGPL
53244  * <script type="text/javascript">
53245  */
53246  
53247 /**
53248  * @class Roo.grid.EditorGrid
53249  * @extends Roo.grid.Grid
53250  * Class for creating and editable grid.
53251  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
53252  * The container MUST have some type of size defined for the grid to fill. The container will be 
53253  * automatically set to position relative if it isn't already.
53254  * @param {Object} dataSource The data model to bind to
53255  * @param {Object} colModel The column model with info about this grid's columns
53256  */
53257 Roo.grid.EditorGrid = function(container, config){
53258     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
53259     this.getGridEl().addClass("xedit-grid");
53260
53261     if(!this.selModel){
53262         this.selModel = new Roo.grid.CellSelectionModel();
53263     }
53264
53265     this.activeEditor = null;
53266
53267         this.addEvents({
53268             /**
53269              * @event beforeedit
53270              * Fires before cell editing is triggered. The edit event object has the following properties <br />
53271              * <ul style="padding:5px;padding-left:16px;">
53272              * <li>grid - This grid</li>
53273              * <li>record - The record being edited</li>
53274              * <li>field - The field name being edited</li>
53275              * <li>value - The value for the field being edited.</li>
53276              * <li>row - The grid row index</li>
53277              * <li>column - The grid column index</li>
53278              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
53279              * </ul>
53280              * @param {Object} e An edit event (see above for description)
53281              */
53282             "beforeedit" : true,
53283             /**
53284              * @event afteredit
53285              * Fires after a cell is edited. <br />
53286              * <ul style="padding:5px;padding-left:16px;">
53287              * <li>grid - This grid</li>
53288              * <li>record - The record being edited</li>
53289              * <li>field - The field name being edited</li>
53290              * <li>value - The value being set</li>
53291              * <li>originalValue - The original value for the field, before the edit.</li>
53292              * <li>row - The grid row index</li>
53293              * <li>column - The grid column index</li>
53294              * </ul>
53295              * @param {Object} e An edit event (see above for description)
53296              */
53297             "afteredit" : true,
53298             /**
53299              * @event validateedit
53300              * Fires after a cell is edited, but before the value is set in the record. 
53301          * You can use this to modify the value being set in the field, Return false
53302              * to cancel the change. The edit event object has the following properties <br />
53303              * <ul style="padding:5px;padding-left:16px;">
53304          * <li>editor - This editor</li>
53305              * <li>grid - This grid</li>
53306              * <li>record - The record being edited</li>
53307              * <li>field - The field name being edited</li>
53308              * <li>value - The value being set</li>
53309              * <li>originalValue - The original value for the field, before the edit.</li>
53310              * <li>row - The grid row index</li>
53311              * <li>column - The grid column index</li>
53312              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
53313              * </ul>
53314              * @param {Object} e An edit event (see above for description)
53315              */
53316             "validateedit" : true
53317         });
53318     this.on("bodyscroll", this.stopEditing,  this);
53319     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
53320 };
53321
53322 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
53323     /**
53324      * @cfg {Number} clicksToEdit
53325      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
53326      */
53327     clicksToEdit: 2,
53328
53329     // private
53330     isEditor : true,
53331     // private
53332     trackMouseOver: false, // causes very odd FF errors
53333
53334     onCellDblClick : function(g, row, col){
53335         this.startEditing(row, col);
53336     },
53337
53338     onEditComplete : function(ed, value, startValue){
53339         this.editing = false;
53340         this.activeEditor = null;
53341         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
53342         var r = ed.record;
53343         var field = this.colModel.getDataIndex(ed.col);
53344         var e = {
53345             grid: this,
53346             record: r,
53347             field: field,
53348             originalValue: startValue,
53349             value: value,
53350             row: ed.row,
53351             column: ed.col,
53352             cancel:false,
53353             editor: ed
53354         };
53355         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
53356         cell.show();
53357           
53358         if(String(value) !== String(startValue)){
53359             
53360             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
53361                 r.set(field, e.value);
53362                 // if we are dealing with a combo box..
53363                 // then we also set the 'name' colum to be the displayField
53364                 if (ed.field.displayField && ed.field.name) {
53365                     r.set(ed.field.name, ed.field.el.dom.value);
53366                 }
53367                 
53368                 delete e.cancel; //?? why!!!
53369                 this.fireEvent("afteredit", e);
53370             }
53371         } else {
53372             this.fireEvent("afteredit", e); // always fire it!
53373         }
53374         this.view.focusCell(ed.row, ed.col);
53375     },
53376
53377     /**
53378      * Starts editing the specified for the specified row/column
53379      * @param {Number} rowIndex
53380      * @param {Number} colIndex
53381      */
53382     startEditing : function(row, col){
53383         this.stopEditing();
53384         if(this.colModel.isCellEditable(col, row)){
53385             this.view.ensureVisible(row, col, true);
53386           
53387             var r = this.dataSource.getAt(row);
53388             var field = this.colModel.getDataIndex(col);
53389             var cell = Roo.get(this.view.getCell(row,col));
53390             var e = {
53391                 grid: this,
53392                 record: r,
53393                 field: field,
53394                 value: r.data[field],
53395                 row: row,
53396                 column: col,
53397                 cancel:false 
53398             };
53399             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
53400                 this.editing = true;
53401                 var ed = this.colModel.getCellEditor(col, row);
53402                 
53403                 if (!ed) {
53404                     return;
53405                 }
53406                 if(!ed.rendered){
53407                     ed.render(ed.parentEl || document.body);
53408                 }
53409                 ed.field.reset();
53410                
53411                 cell.hide();
53412                 
53413                 (function(){ // complex but required for focus issues in safari, ie and opera
53414                     ed.row = row;
53415                     ed.col = col;
53416                     ed.record = r;
53417                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
53418                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
53419                     this.activeEditor = ed;
53420                     var v = r.data[field];
53421                     ed.startEdit(this.view.getCell(row, col), v);
53422                     // combo's with 'displayField and name set
53423                     if (ed.field.displayField && ed.field.name) {
53424                         ed.field.el.dom.value = r.data[ed.field.name];
53425                     }
53426                     
53427                     
53428                 }).defer(50, this);
53429             }
53430         }
53431     },
53432         
53433     /**
53434      * Stops any active editing
53435      */
53436     stopEditing : function(){
53437         if(this.activeEditor){
53438             this.activeEditor.completeEdit();
53439         }
53440         this.activeEditor = null;
53441     },
53442         
53443          /**
53444      * Called to get grid's drag proxy text, by default returns this.ddText.
53445      * @return {String}
53446      */
53447     getDragDropText : function(){
53448         var count = this.selModel.getSelectedCell() ? 1 : 0;
53449         return String.format(this.ddText, count, count == 1 ? '' : 's');
53450     }
53451         
53452 });/*
53453  * Based on:
53454  * Ext JS Library 1.1.1
53455  * Copyright(c) 2006-2007, Ext JS, LLC.
53456  *
53457  * Originally Released Under LGPL - original licence link has changed is not relivant.
53458  *
53459  * Fork - LGPL
53460  * <script type="text/javascript">
53461  */
53462
53463 // private - not really -- you end up using it !
53464 // This is a support class used internally by the Grid components
53465
53466 /**
53467  * @class Roo.grid.GridEditor
53468  * @extends Roo.Editor
53469  * Class for creating and editable grid elements.
53470  * @param {Object} config any settings (must include field)
53471  */
53472 Roo.grid.GridEditor = function(field, config){
53473     if (!config && field.field) {
53474         config = field;
53475         field = Roo.factory(config.field, Roo.form);
53476     }
53477     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
53478     field.monitorTab = false;
53479 };
53480
53481 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
53482     
53483     /**
53484      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
53485      */
53486     
53487     alignment: "tl-tl",
53488     autoSize: "width",
53489     hideEl : false,
53490     cls: "x-small-editor x-grid-editor",
53491     shim:false,
53492     shadow:"frame"
53493 });/*
53494  * Based on:
53495  * Ext JS Library 1.1.1
53496  * Copyright(c) 2006-2007, Ext JS, LLC.
53497  *
53498  * Originally Released Under LGPL - original licence link has changed is not relivant.
53499  *
53500  * Fork - LGPL
53501  * <script type="text/javascript">
53502  */
53503   
53504
53505   
53506 Roo.grid.PropertyRecord = Roo.data.Record.create([
53507     {name:'name',type:'string'},  'value'
53508 ]);
53509
53510
53511 Roo.grid.PropertyStore = function(grid, source){
53512     this.grid = grid;
53513     this.store = new Roo.data.Store({
53514         recordType : Roo.grid.PropertyRecord
53515     });
53516     this.store.on('update', this.onUpdate,  this);
53517     if(source){
53518         this.setSource(source);
53519     }
53520     Roo.grid.PropertyStore.superclass.constructor.call(this);
53521 };
53522
53523
53524
53525 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
53526     setSource : function(o){
53527         this.source = o;
53528         this.store.removeAll();
53529         var data = [];
53530         for(var k in o){
53531             if(this.isEditableValue(o[k])){
53532                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
53533             }
53534         }
53535         this.store.loadRecords({records: data}, {}, true);
53536     },
53537
53538     onUpdate : function(ds, record, type){
53539         if(type == Roo.data.Record.EDIT){
53540             var v = record.data['value'];
53541             var oldValue = record.modified['value'];
53542             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
53543                 this.source[record.id] = v;
53544                 record.commit();
53545                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
53546             }else{
53547                 record.reject();
53548             }
53549         }
53550     },
53551
53552     getProperty : function(row){
53553        return this.store.getAt(row);
53554     },
53555
53556     isEditableValue: function(val){
53557         if(val && val instanceof Date){
53558             return true;
53559         }else if(typeof val == 'object' || typeof val == 'function'){
53560             return false;
53561         }
53562         return true;
53563     },
53564
53565     setValue : function(prop, value){
53566         this.source[prop] = value;
53567         this.store.getById(prop).set('value', value);
53568     },
53569
53570     getSource : function(){
53571         return this.source;
53572     }
53573 });
53574
53575 Roo.grid.PropertyColumnModel = function(grid, store){
53576     this.grid = grid;
53577     var g = Roo.grid;
53578     g.PropertyColumnModel.superclass.constructor.call(this, [
53579         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
53580         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
53581     ]);
53582     this.store = store;
53583     this.bselect = Roo.DomHelper.append(document.body, {
53584         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
53585             {tag: 'option', value: 'true', html: 'true'},
53586             {tag: 'option', value: 'false', html: 'false'}
53587         ]
53588     });
53589     Roo.id(this.bselect);
53590     var f = Roo.form;
53591     this.editors = {
53592         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
53593         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
53594         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
53595         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
53596         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
53597     };
53598     this.renderCellDelegate = this.renderCell.createDelegate(this);
53599     this.renderPropDelegate = this.renderProp.createDelegate(this);
53600 };
53601
53602 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
53603     
53604     
53605     nameText : 'Name',
53606     valueText : 'Value',
53607     
53608     dateFormat : 'm/j/Y',
53609     
53610     
53611     renderDate : function(dateVal){
53612         return dateVal.dateFormat(this.dateFormat);
53613     },
53614
53615     renderBool : function(bVal){
53616         return bVal ? 'true' : 'false';
53617     },
53618
53619     isCellEditable : function(colIndex, rowIndex){
53620         return colIndex == 1;
53621     },
53622
53623     getRenderer : function(col){
53624         return col == 1 ?
53625             this.renderCellDelegate : this.renderPropDelegate;
53626     },
53627
53628     renderProp : function(v){
53629         return this.getPropertyName(v);
53630     },
53631
53632     renderCell : function(val){
53633         var rv = val;
53634         if(val instanceof Date){
53635             rv = this.renderDate(val);
53636         }else if(typeof val == 'boolean'){
53637             rv = this.renderBool(val);
53638         }
53639         return Roo.util.Format.htmlEncode(rv);
53640     },
53641
53642     getPropertyName : function(name){
53643         var pn = this.grid.propertyNames;
53644         return pn && pn[name] ? pn[name] : name;
53645     },
53646
53647     getCellEditor : function(colIndex, rowIndex){
53648         var p = this.store.getProperty(rowIndex);
53649         var n = p.data['name'], val = p.data['value'];
53650         
53651         if(typeof(this.grid.customEditors[n]) == 'string'){
53652             return this.editors[this.grid.customEditors[n]];
53653         }
53654         if(typeof(this.grid.customEditors[n]) != 'undefined'){
53655             return this.grid.customEditors[n];
53656         }
53657         if(val instanceof Date){
53658             return this.editors['date'];
53659         }else if(typeof val == 'number'){
53660             return this.editors['number'];
53661         }else if(typeof val == 'boolean'){
53662             return this.editors['boolean'];
53663         }else{
53664             return this.editors['string'];
53665         }
53666     }
53667 });
53668
53669 /**
53670  * @class Roo.grid.PropertyGrid
53671  * @extends Roo.grid.EditorGrid
53672  * This class represents the  interface of a component based property grid control.
53673  * <br><br>Usage:<pre><code>
53674  var grid = new Roo.grid.PropertyGrid("my-container-id", {
53675       
53676  });
53677  // set any options
53678  grid.render();
53679  * </code></pre>
53680   
53681  * @constructor
53682  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
53683  * The container MUST have some type of size defined for the grid to fill. The container will be
53684  * automatically set to position relative if it isn't already.
53685  * @param {Object} config A config object that sets properties on this grid.
53686  */
53687 Roo.grid.PropertyGrid = function(container, config){
53688     config = config || {};
53689     var store = new Roo.grid.PropertyStore(this);
53690     this.store = store;
53691     var cm = new Roo.grid.PropertyColumnModel(this, store);
53692     store.store.sort('name', 'ASC');
53693     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
53694         ds: store.store,
53695         cm: cm,
53696         enableColLock:false,
53697         enableColumnMove:false,
53698         stripeRows:false,
53699         trackMouseOver: false,
53700         clicksToEdit:1
53701     }, config));
53702     this.getGridEl().addClass('x-props-grid');
53703     this.lastEditRow = null;
53704     this.on('columnresize', this.onColumnResize, this);
53705     this.addEvents({
53706          /**
53707              * @event beforepropertychange
53708              * Fires before a property changes (return false to stop?)
53709              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
53710              * @param {String} id Record Id
53711              * @param {String} newval New Value
53712          * @param {String} oldval Old Value
53713              */
53714         "beforepropertychange": true,
53715         /**
53716              * @event propertychange
53717              * Fires after a property changes
53718              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
53719              * @param {String} id Record Id
53720              * @param {String} newval New Value
53721          * @param {String} oldval Old Value
53722              */
53723         "propertychange": true
53724     });
53725     this.customEditors = this.customEditors || {};
53726 };
53727 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
53728     
53729      /**
53730      * @cfg {Object} customEditors map of colnames=> custom editors.
53731      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
53732      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
53733      * false disables editing of the field.
53734          */
53735     
53736       /**
53737      * @cfg {Object} propertyNames map of property Names to their displayed value
53738          */
53739     
53740     render : function(){
53741         Roo.grid.PropertyGrid.superclass.render.call(this);
53742         this.autoSize.defer(100, this);
53743     },
53744
53745     autoSize : function(){
53746         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
53747         if(this.view){
53748             this.view.fitColumns();
53749         }
53750     },
53751
53752     onColumnResize : function(){
53753         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
53754         this.autoSize();
53755     },
53756     /**
53757      * Sets the data for the Grid
53758      * accepts a Key => Value object of all the elements avaiable.
53759      * @param {Object} data  to appear in grid.
53760      */
53761     setSource : function(source){
53762         this.store.setSource(source);
53763         //this.autoSize();
53764     },
53765     /**
53766      * Gets all the data from the grid.
53767      * @return {Object} data  data stored in grid
53768      */
53769     getSource : function(){
53770         return this.store.getSource();
53771     }
53772 });/*
53773  * Based on:
53774  * Ext JS Library 1.1.1
53775  * Copyright(c) 2006-2007, Ext JS, LLC.
53776  *
53777  * Originally Released Under LGPL - original licence link has changed is not relivant.
53778  *
53779  * Fork - LGPL
53780  * <script type="text/javascript">
53781  */
53782  
53783 /**
53784  * @class Roo.LoadMask
53785  * A simple utility class for generically masking elements while loading data.  If the element being masked has
53786  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
53787  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
53788  * element's UpdateManager load indicator and will be destroyed after the initial load.
53789  * @constructor
53790  * Create a new LoadMask
53791  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
53792  * @param {Object} config The config object
53793  */
53794 Roo.LoadMask = function(el, config){
53795     this.el = Roo.get(el);
53796     Roo.apply(this, config);
53797     if(this.store){
53798         this.store.on('beforeload', this.onBeforeLoad, this);
53799         this.store.on('load', this.onLoad, this);
53800         this.store.on('loadexception', this.onLoadException, this);
53801         this.removeMask = false;
53802     }else{
53803         var um = this.el.getUpdateManager();
53804         um.showLoadIndicator = false; // disable the default indicator
53805         um.on('beforeupdate', this.onBeforeLoad, this);
53806         um.on('update', this.onLoad, this);
53807         um.on('failure', this.onLoad, this);
53808         this.removeMask = true;
53809     }
53810 };
53811
53812 Roo.LoadMask.prototype = {
53813     /**
53814      * @cfg {Boolean} removeMask
53815      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
53816      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
53817      */
53818     /**
53819      * @cfg {String} msg
53820      * The text to display in a centered loading message box (defaults to 'Loading...')
53821      */
53822     msg : 'Loading...',
53823     /**
53824      * @cfg {String} msgCls
53825      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
53826      */
53827     msgCls : 'x-mask-loading',
53828
53829     /**
53830      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
53831      * @type Boolean
53832      */
53833     disabled: false,
53834
53835     /**
53836      * Disables the mask to prevent it from being displayed
53837      */
53838     disable : function(){
53839        this.disabled = true;
53840     },
53841
53842     /**
53843      * Enables the mask so that it can be displayed
53844      */
53845     enable : function(){
53846         this.disabled = false;
53847     },
53848     
53849     onLoadException : function()
53850     {
53851         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53852             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53853         }
53854         this.el.unmask(this.removeMask);
53855     },
53856     // private
53857     onLoad : function()
53858     {
53859         this.el.unmask(this.removeMask);
53860     },
53861
53862     // private
53863     onBeforeLoad : function(){
53864         if(!this.disabled){
53865             this.el.mask(this.msg, this.msgCls);
53866         }
53867     },
53868
53869     // private
53870     destroy : function(){
53871         if(this.store){
53872             this.store.un('beforeload', this.onBeforeLoad, this);
53873             this.store.un('load', this.onLoad, this);
53874             this.store.un('loadexception', this.onLoadException, this);
53875         }else{
53876             var um = this.el.getUpdateManager();
53877             um.un('beforeupdate', this.onBeforeLoad, this);
53878             um.un('update', this.onLoad, this);
53879             um.un('failure', this.onLoad, this);
53880         }
53881     }
53882 };/*
53883  * Based on:
53884  * Ext JS Library 1.1.1
53885  * Copyright(c) 2006-2007, Ext JS, LLC.
53886  *
53887  * Originally Released Under LGPL - original licence link has changed is not relivant.
53888  *
53889  * Fork - LGPL
53890  * <script type="text/javascript">
53891  */
53892
53893
53894 /**
53895  * @class Roo.XTemplate
53896  * @extends Roo.Template
53897  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
53898 <pre><code>
53899 var t = new Roo.XTemplate(
53900         '&lt;select name="{name}"&gt;',
53901                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
53902         '&lt;/select&gt;'
53903 );
53904  
53905 // then append, applying the master template values
53906  </code></pre>
53907  *
53908  * Supported features:
53909  *
53910  *  Tags:
53911
53912 <pre><code>
53913       {a_variable} - output encoded.
53914       {a_variable.format:("Y-m-d")} - call a method on the variable
53915       {a_variable:raw} - unencoded output
53916       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
53917       {a_variable:this.method_on_template(...)} - call a method on the template object.
53918  
53919 </code></pre>
53920  *  The tpl tag:
53921 <pre><code>
53922         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
53923         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
53924         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
53925         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
53926   
53927         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
53928         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
53929 </code></pre>
53930  *      
53931  */
53932 Roo.XTemplate = function()
53933 {
53934     Roo.XTemplate.superclass.constructor.apply(this, arguments);
53935     if (this.html) {
53936         this.compile();
53937     }
53938 };
53939
53940
53941 Roo.extend(Roo.XTemplate, Roo.Template, {
53942
53943     /**
53944      * The various sub templates
53945      */
53946     tpls : false,
53947     /**
53948      *
53949      * basic tag replacing syntax
53950      * WORD:WORD()
53951      *
53952      * // you can fake an object call by doing this
53953      *  x.t:(test,tesT) 
53954      * 
53955      */
53956     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
53957
53958     /**
53959      * compile the template
53960      *
53961      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
53962      *
53963      */
53964     compile: function()
53965     {
53966         var s = this.html;
53967      
53968         s = ['<tpl>', s, '</tpl>'].join('');
53969     
53970         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
53971             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
53972             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
53973             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
53974             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
53975             m,
53976             id     = 0,
53977             tpls   = [];
53978     
53979         while(true == !!(m = s.match(re))){
53980             var forMatch   = m[0].match(nameRe),
53981                 ifMatch   = m[0].match(ifRe),
53982                 execMatch   = m[0].match(execRe),
53983                 namedMatch   = m[0].match(namedRe),
53984                 
53985                 exp  = null, 
53986                 fn   = null,
53987                 exec = null,
53988                 name = forMatch && forMatch[1] ? forMatch[1] : '';
53989                 
53990             if (ifMatch) {
53991                 // if - puts fn into test..
53992                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
53993                 if(exp){
53994                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
53995                 }
53996             }
53997             
53998             if (execMatch) {
53999                 // exec - calls a function... returns empty if true is  returned.
54000                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
54001                 if(exp){
54002                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
54003                 }
54004             }
54005             
54006             
54007             if (name) {
54008                 // for = 
54009                 switch(name){
54010                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
54011                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
54012                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
54013                 }
54014             }
54015             var uid = namedMatch ? namedMatch[1] : id;
54016             
54017             
54018             tpls.push({
54019                 id:     namedMatch ? namedMatch[1] : id,
54020                 target: name,
54021                 exec:   exec,
54022                 test:   fn,
54023                 body:   m[1] || ''
54024             });
54025             if (namedMatch) {
54026                 s = s.replace(m[0], '');
54027             } else { 
54028                 s = s.replace(m[0], '{xtpl'+ id + '}');
54029             }
54030             ++id;
54031         }
54032         this.tpls = [];
54033         for(var i = tpls.length-1; i >= 0; --i){
54034             this.compileTpl(tpls[i]);
54035             this.tpls[tpls[i].id] = tpls[i];
54036         }
54037         this.master = tpls[tpls.length-1];
54038         return this;
54039     },
54040     /**
54041      * same as applyTemplate, except it's done to one of the subTemplates
54042      * when using named templates, you can do:
54043      *
54044      * var str = pl.applySubTemplate('your-name', values);
54045      *
54046      * 
54047      * @param {Number} id of the template
54048      * @param {Object} values to apply to template
54049      * @param {Object} parent (normaly the instance of this object)
54050      */
54051     applySubTemplate : function(id, values, parent)
54052     {
54053         
54054         
54055         var t = this.tpls[id];
54056         
54057         
54058         try { 
54059             if(t.test && !t.test.call(this, values, parent)){
54060                 return '';
54061             }
54062         } catch(e) {
54063             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
54064             Roo.log(e.toString());
54065             Roo.log(t.test);
54066             return ''
54067         }
54068         try { 
54069             
54070             if(t.exec && t.exec.call(this, values, parent)){
54071                 return '';
54072             }
54073         } catch(e) {
54074             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
54075             Roo.log(e.toString());
54076             Roo.log(t.exec);
54077             return ''
54078         }
54079         try {
54080             var vs = t.target ? t.target.call(this, values, parent) : values;
54081             parent = t.target ? values : parent;
54082             if(t.target && vs instanceof Array){
54083                 var buf = [];
54084                 for(var i = 0, len = vs.length; i < len; i++){
54085                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
54086                 }
54087                 return buf.join('');
54088             }
54089             return t.compiled.call(this, vs, parent);
54090         } catch (e) {
54091             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
54092             Roo.log(e.toString());
54093             Roo.log(t.compiled);
54094             return '';
54095         }
54096     },
54097
54098     compileTpl : function(tpl)
54099     {
54100         var fm = Roo.util.Format;
54101         var useF = this.disableFormats !== true;
54102         var sep = Roo.isGecko ? "+" : ",";
54103         var undef = function(str) {
54104             Roo.log("Property not found :"  + str);
54105             return '';
54106         };
54107         
54108         var fn = function(m, name, format, args)
54109         {
54110             //Roo.log(arguments);
54111             args = args ? args.replace(/\\'/g,"'") : args;
54112             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
54113             if (typeof(format) == 'undefined') {
54114                 format= 'htmlEncode';
54115             }
54116             if (format == 'raw' ) {
54117                 format = false;
54118             }
54119             
54120             if(name.substr(0, 4) == 'xtpl'){
54121                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
54122             }
54123             
54124             // build an array of options to determine if value is undefined..
54125             
54126             // basically get 'xxxx.yyyy' then do
54127             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
54128             //    (function () { Roo.log("Property not found"); return ''; })() :
54129             //    ......
54130             
54131             var udef_ar = [];
54132             var lookfor = '';
54133             Roo.each(name.split('.'), function(st) {
54134                 lookfor += (lookfor.length ? '.': '') + st;
54135                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
54136             });
54137             
54138             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
54139             
54140             
54141             if(format && useF){
54142                 
54143                 args = args ? ',' + args : "";
54144                  
54145                 if(format.substr(0, 5) != "this."){
54146                     format = "fm." + format + '(';
54147                 }else{
54148                     format = 'this.call("'+ format.substr(5) + '", ';
54149                     args = ", values";
54150                 }
54151                 
54152                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
54153             }
54154              
54155             if (args.length) {
54156                 // called with xxyx.yuu:(test,test)
54157                 // change to ()
54158                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
54159             }
54160             // raw.. - :raw modifier..
54161             return "'"+ sep + udef_st  + name + ")"+sep+"'";
54162             
54163         };
54164         var body;
54165         // branched to use + in gecko and [].join() in others
54166         if(Roo.isGecko){
54167             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
54168                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
54169                     "';};};";
54170         }else{
54171             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
54172             body.push(tpl.body.replace(/(\r\n|\n)/g,
54173                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
54174             body.push("'].join('');};};");
54175             body = body.join('');
54176         }
54177         
54178         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
54179        
54180         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
54181         eval(body);
54182         
54183         return this;
54184     },
54185
54186     applyTemplate : function(values){
54187         return this.master.compiled.call(this, values, {});
54188         //var s = this.subs;
54189     },
54190
54191     apply : function(){
54192         return this.applyTemplate.apply(this, arguments);
54193     }
54194
54195  });
54196
54197 Roo.XTemplate.from = function(el){
54198     el = Roo.getDom(el);
54199     return new Roo.XTemplate(el.value || el.innerHTML);
54200 };/*
54201  * Original code for Roojs - LGPL
54202  * <script type="text/javascript">
54203  */
54204  
54205 /**
54206  * @class Roo.XComponent
54207  * A delayed Element creator...
54208  * Or a way to group chunks of interface together.
54209  * 
54210  * Mypart.xyx = new Roo.XComponent({
54211
54212     parent : 'Mypart.xyz', // empty == document.element.!!
54213     order : '001',
54214     name : 'xxxx'
54215     region : 'xxxx'
54216     disabled : function() {} 
54217      
54218     tree : function() { // return an tree of xtype declared components
54219         var MODULE = this;
54220         return 
54221         {
54222             xtype : 'NestedLayoutPanel',
54223             // technicall
54224         }
54225      ]
54226  *})
54227  *
54228  *
54229  * It can be used to build a big heiracy, with parent etc.
54230  * or you can just use this to render a single compoent to a dom element
54231  * MYPART.render(Roo.Element | String(id) | dom_element )
54232  * 
54233  * @extends Roo.util.Observable
54234  * @constructor
54235  * @param cfg {Object} configuration of component
54236  * 
54237  */
54238 Roo.XComponent = function(cfg) {
54239     Roo.apply(this, cfg);
54240     this.addEvents({ 
54241         /**
54242              * @event built
54243              * Fires when this the componnt is built
54244              * @param {Roo.XComponent} c the component
54245              */
54246         'built' : true
54247         
54248     });
54249     this.region = this.region || 'center'; // default..
54250     Roo.XComponent.register(this);
54251     this.modules = false;
54252     this.el = false; // where the layout goes..
54253     
54254     
54255 }
54256 Roo.extend(Roo.XComponent, Roo.util.Observable, {
54257     /**
54258      * @property el
54259      * The created element (with Roo.factory())
54260      * @type {Roo.Layout}
54261      */
54262     el  : false,
54263     
54264     /**
54265      * @property el
54266      * for BC  - use el in new code
54267      * @type {Roo.Layout}
54268      */
54269     panel : false,
54270     
54271     /**
54272      * @property layout
54273      * for BC  - use el in new code
54274      * @type {Roo.Layout}
54275      */
54276     layout : false,
54277     
54278      /**
54279      * @cfg {Function|boolean} disabled
54280      * If this module is disabled by some rule, return true from the funtion
54281      */
54282     disabled : false,
54283     
54284     /**
54285      * @cfg {String} parent 
54286      * Name of parent element which it get xtype added to..
54287      */
54288     parent: false,
54289     
54290     /**
54291      * @cfg {String} order
54292      * Used to set the order in which elements are created (usefull for multiple tabs)
54293      */
54294     
54295     order : false,
54296     /**
54297      * @cfg {String} name
54298      * String to display while loading.
54299      */
54300     name : false,
54301     /**
54302      * @cfg {String} region
54303      * Region to render component to (defaults to center)
54304      */
54305     region : 'center',
54306     
54307     /**
54308      * @cfg {Array} items
54309      * A single item array - the first element is the root of the tree..
54310      * It's done this way to stay compatible with the Xtype system...
54311      */
54312     items : false,
54313     
54314     /**
54315      * @property _tree
54316      * The method that retuns the tree of parts that make up this compoennt 
54317      * @type {function}
54318      */
54319     _tree  : false,
54320     
54321      /**
54322      * render
54323      * render element to dom or tree
54324      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
54325      */
54326     
54327     render : function(el)
54328     {
54329         
54330         el = el || false;
54331         var hp = this.parent ? 1 : 0;
54332         
54333         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
54334             // if parent is a '#.....' string, then let's use that..
54335             var ename = this.parent.substr(1)
54336             this.parent = false;
54337             el = Roo.get(ename);
54338             if (!el) {
54339                 Roo.log("Warning - element can not be found :#" + ename );
54340                 return;
54341             }
54342         }
54343         
54344         
54345         if (!this.parent) {
54346             
54347             el = el ? Roo.get(el) : false;      
54348             
54349             // it's a top level one..
54350             this.parent =  {
54351                 el : new Roo.BorderLayout(el || document.body, {
54352                 
54353                      center: {
54354                          titlebar: false,
54355                          autoScroll:false,
54356                          closeOnTab: true,
54357                          tabPosition: 'top',
54358                           //resizeTabs: true,
54359                          alwaysShowTabs: el && hp? false :  true,
54360                          hideTabs: el || !hp ? true :  false,
54361                          minTabWidth: 140
54362                      }
54363                  })
54364             }
54365         }
54366         
54367                 if (!this.parent.el) {
54368                         // probably an old style ctor, which has been disabled.
54369                         return;
54370                         
54371                 }
54372                 // The 'tree' method is  '_tree now' 
54373             
54374         var tree = this._tree ? this._tree() : this.tree();
54375         tree.region = tree.region || this.region;
54376         this.el = this.parent.el.addxtype(tree);
54377         this.fireEvent('built', this);
54378         
54379         this.panel = this.el;
54380         this.layout = this.panel.layout;
54381                 this.parentLayout = this.parent.layout  || false;  
54382          
54383     }
54384     
54385 });
54386
54387 Roo.apply(Roo.XComponent, {
54388     /**
54389      * @property  hideProgress
54390      * true to disable the building progress bar.. usefull on single page renders.
54391      * @type Boolean
54392      */
54393     hideProgress : false,
54394     /**
54395      * @property  buildCompleted
54396      * True when the builder has completed building the interface.
54397      * @type Boolean
54398      */
54399     buildCompleted : false,
54400      
54401     /**
54402      * @property  topModule
54403      * the upper most module - uses document.element as it's constructor.
54404      * @type Object
54405      */
54406      
54407     topModule  : false,
54408       
54409     /**
54410      * @property  modules
54411      * array of modules to be created by registration system.
54412      * @type {Array} of Roo.XComponent
54413      */
54414     
54415     modules : [],
54416     /**
54417      * @property  elmodules
54418      * array of modules to be created by which use #ID 
54419      * @type {Array} of Roo.XComponent
54420      */
54421      
54422     elmodules : [],
54423
54424     
54425     /**
54426      * Register components to be built later.
54427      *
54428      * This solves the following issues
54429      * - Building is not done on page load, but after an authentication process has occured.
54430      * - Interface elements are registered on page load
54431      * - Parent Interface elements may not be loaded before child, so this handles that..
54432      * 
54433      *
54434      * example:
54435      * 
54436      * MyApp.register({
54437           order : '000001',
54438           module : 'Pman.Tab.projectMgr',
54439           region : 'center',
54440           parent : 'Pman.layout',
54441           disabled : false,  // or use a function..
54442         })
54443      
54444      * * @param {Object} details about module
54445      */
54446     register : function(obj) {
54447                 
54448         Roo.XComponent.event.fireEvent('register', obj);
54449         switch(typeof(obj.disabled) ) {
54450                 
54451             case 'undefined':
54452                 break;
54453             
54454             case 'function':
54455                 if ( obj.disabled() ) {
54456                         return;
54457                 }
54458                 break;
54459             
54460             default:
54461                 if (obj.disabled) {
54462                         return;
54463                 }
54464                 break;
54465         }
54466                 
54467         this.modules.push(obj);
54468          
54469     },
54470     /**
54471      * convert a string to an object..
54472      * eg. 'AAA.BBB' -> finds AAA.BBB
54473
54474      */
54475     
54476     toObject : function(str)
54477     {
54478         if (!str || typeof(str) == 'object') {
54479             return str;
54480         }
54481         if (str.substring(0,1) == '#') {
54482             return str;
54483         }
54484
54485         var ar = str.split('.');
54486         var rt, o;
54487         rt = ar.shift();
54488             /** eval:var:o */
54489         try {
54490             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
54491         } catch (e) {
54492             throw "Module not found : " + str;
54493         }
54494         
54495         if (o === false) {
54496             throw "Module not found : " + str;
54497         }
54498         Roo.each(ar, function(e) {
54499             if (typeof(o[e]) == 'undefined') {
54500                 throw "Module not found : " + str;
54501             }
54502             o = o[e];
54503         });
54504         
54505         return o;
54506         
54507     },
54508     
54509     
54510     /**
54511      * move modules into their correct place in the tree..
54512      * 
54513      */
54514     preBuild : function ()
54515     {
54516         var _t = this;
54517         Roo.each(this.modules , function (obj)
54518         {
54519             Roo.XComponent.event.fireEvent('beforebuild', obj);
54520             
54521             var opar = obj.parent;
54522             try { 
54523                 obj.parent = this.toObject(opar);
54524             } catch(e) {
54525                 Roo.log("parent:toObject failed: " + e.toString());
54526                 return;
54527             }
54528             
54529             if (!obj.parent) {
54530                 Roo.debug && Roo.log("GOT top level module");
54531                 Roo.debug && Roo.log(obj);
54532                 obj.modules = new Roo.util.MixedCollection(false, 
54533                     function(o) { return o.order + '' }
54534                 );
54535                 this.topModule = obj;
54536                 return;
54537             }
54538                         // parent is a string (usually a dom element name..)
54539             if (typeof(obj.parent) == 'string') {
54540                 this.elmodules.push(obj);
54541                 return;
54542             }
54543             if (obj.parent.constructor != Roo.XComponent) {
54544                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
54545             }
54546             if (!obj.parent.modules) {
54547                 obj.parent.modules = new Roo.util.MixedCollection(false, 
54548                     function(o) { return o.order + '' }
54549                 );
54550             }
54551             if (obj.parent.disabled) {
54552                 obj.disabled = true;
54553             }
54554             obj.parent.modules.add(obj);
54555         }, this);
54556     },
54557     
54558      /**
54559      * make a list of modules to build.
54560      * @return {Array} list of modules. 
54561      */ 
54562     
54563     buildOrder : function()
54564     {
54565         var _this = this;
54566         var cmp = function(a,b) {   
54567             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
54568         };
54569         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
54570             throw "No top level modules to build";
54571         }
54572         
54573         // make a flat list in order of modules to build.
54574         var mods = this.topModule ? [ this.topModule ] : [];
54575                 
54576         // elmodules (is a list of DOM based modules )
54577         Roo.each(this.elmodules, function(e) {
54578             mods.push(e)
54579         });
54580
54581         
54582         // add modules to their parents..
54583         var addMod = function(m) {
54584             Roo.debug && Roo.log("build Order: add: " + m.name);
54585             
54586         mods.push(m);
54587         if (m.modules && !m.disabled) {
54588             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
54589             m.modules.keySort('ASC',  cmp );
54590             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
54591
54592             m.modules.each(addMod);
54593         } else {
54594             Roo.debug && Roo.log("build Order: no child modules");
54595             }
54596             // not sure if this is used any more..
54597             if (m.finalize) {
54598                 m.finalize.name = m.name + " (clean up) ";
54599                 mods.push(m.finalize);
54600             }
54601             
54602         }
54603         if (this.topModule) { 
54604             this.topModule.modules.keySort('ASC',  cmp );
54605             this.topModule.modules.each(addMod);
54606         }
54607         return mods;
54608     },
54609     
54610      /**
54611      * Build the registered modules.
54612      * @param {Object} parent element.
54613      * @param {Function} optional method to call after module has been added.
54614      * 
54615      */ 
54616    
54617     build : function() 
54618     {
54619         
54620         this.preBuild();
54621         var mods = this.buildOrder();
54622       
54623         //this.allmods = mods;
54624         //Roo.debug && Roo.log(mods);
54625         //return;
54626         if (!mods.length) { // should not happen
54627             throw "NO modules!!!";
54628         }
54629         
54630         
54631         var msg = "Building Interface...";
54632         // flash it up as modal - so we store the mask!?
54633         if (!this.hideProgress) {
54634             Roo.MessageBox.show({ title: 'loading' });
54635             Roo.MessageBox.show({
54636                title: "Please wait...",
54637                msg: msg,
54638                width:450,
54639                progress:true,
54640                closable:false,
54641                modal: false
54642               
54643             });
54644         }
54645         var total = mods.length;
54646         
54647         var _this = this;
54648         var progressRun = function() {
54649             if (!mods.length) {
54650                 Roo.debug && Roo.log('hide?');
54651                 if (!this.hideProgress) {
54652                     Roo.MessageBox.hide();
54653                 }
54654                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
54655                 
54656                 // THE END...
54657                 return false;   
54658             }
54659             
54660             var m = mods.shift();
54661             
54662             
54663             Roo.debug && Roo.log(m);
54664             // not sure if this is supported any more.. - modules that are are just function
54665             if (typeof(m) == 'function') { 
54666                 m.call(this);
54667                 return progressRun.defer(10, _this);
54668             } 
54669             
54670             
54671             msg = "Building Interface " + (total  - mods.length) + 
54672                     " of " + total + 
54673                     (m.name ? (' - ' + m.name) : '');
54674                         Roo.debug && Roo.log(msg);
54675             if (!this.hideProgress) { 
54676                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
54677             }
54678             
54679          
54680             // is the module disabled?
54681             var disabled = (typeof(m.disabled) == 'function') ?
54682                 m.disabled.call(m.module.disabled) : m.disabled;    
54683             
54684             
54685             if (disabled) {
54686                 return progressRun(); // we do not update the display!
54687             }
54688             
54689             // now build 
54690             
54691                         
54692                         
54693             m.render();
54694             // it's 10 on top level, and 1 on others??? why...
54695             return progressRun.defer(10, _this);
54696              
54697         }
54698         progressRun.defer(1, _this);
54699      
54700         
54701         
54702     },
54703         
54704         
54705         /**
54706          * Event Object.
54707          *
54708          *
54709          */
54710         event: false, 
54711     /**
54712          * wrapper for event.on - aliased later..  
54713          * Typically use to register a event handler for register:
54714          *
54715          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
54716          *
54717          */
54718     on : false
54719    
54720     
54721     
54722 });
54723
54724 Roo.XComponent.event = new Roo.util.Observable({
54725                 events : { 
54726                         /**
54727                          * @event register
54728                          * Fires when an Component is registered,
54729                          * set the disable property on the Component to stop registration.
54730                          * @param {Roo.XComponent} c the component being registerd.
54731                          * 
54732                          */
54733                         'register' : true,
54734             /**
54735                          * @event beforebuild
54736                          * Fires before each Component is built
54737                          * can be used to apply permissions.
54738                          * @param {Roo.XComponent} c the component being registerd.
54739                          * 
54740                          */
54741                         'beforebuild' : true,
54742                         /**
54743                          * @event buildcomplete
54744                          * Fires on the top level element when all elements have been built
54745                          * @param {Roo.XComponent} the top level component.
54746                          */
54747                         'buildcomplete' : true
54748                         
54749                 }
54750 });
54751
54752 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
54753  //<script type="text/javascript">
54754
54755
54756 /**
54757  * @class Roo.Login
54758  * @extends Roo.LayoutDialog
54759  * A generic Login Dialog..... - only one needed in theory!?!?
54760  *
54761  * Fires XComponent builder on success...
54762  * 
54763  * Sends 
54764  *    username,password, lang = for login actions.
54765  *    check = 1 for periodic checking that sesion is valid.
54766  *    passwordRequest = email request password
54767  *    logout = 1 = to logout
54768  * 
54769  * Affects: (this id="????" elements)
54770  *   loading  (removed) (used to indicate application is loading)
54771  *   loading-mask (hides) (used to hide application when it's building loading)
54772  *   
54773  * 
54774  * Usage: 
54775  *    
54776  * 
54777  * Myapp.login = Roo.Login({
54778      url: xxxx,
54779    
54780      realm : 'Myapp', 
54781      
54782      
54783      method : 'POST',
54784      
54785      
54786      * 
54787  })
54788  * 
54789  * 
54790  * 
54791  **/
54792  
54793 Roo.Login = function(cfg)
54794 {
54795     this.addEvents({
54796         'refreshed' : true
54797     });
54798     
54799     Roo.apply(this,cfg);
54800     
54801     Roo.onReady(function() {
54802         this.onLoad();
54803     }, this);
54804     // call parent..
54805     
54806    
54807     Roo.Login.superclass.constructor.call(this, this);
54808     //this.addxtype(this.items[0]);
54809     
54810     
54811 }
54812
54813
54814 Roo.extend(Roo.Login, Roo.LayoutDialog, {
54815     
54816     /**
54817      * @cfg {String} method
54818      * Method used to query for login details.
54819      */
54820     
54821     method : 'POST',
54822     /**
54823      * @cfg {String} url
54824      * URL to query login data. - eg. baseURL + '/Login.php'
54825      */
54826     url : '',
54827     
54828     /**
54829      * @property user
54830      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
54831      * @type {Object} 
54832      */
54833     user : false,
54834     /**
54835      * @property checkFails
54836      * Number of times we have attempted to get authentication check, and failed.
54837      * @type {Number} 
54838      */
54839     checkFails : 0,
54840       /**
54841      * @property intervalID
54842      * The window interval that does the constant login checking.
54843      * @type {Number} 
54844      */
54845     intervalID : 0,
54846     
54847     
54848     onLoad : function() // called on page load...
54849     {
54850         // load 
54851          
54852         if (Roo.get('loading')) { // clear any loading indicator..
54853             Roo.get('loading').remove();
54854         }
54855         
54856         //this.switchLang('en'); // set the language to english..
54857        
54858         this.check({
54859             success:  function(response, opts)  {  // check successfull...
54860             
54861                 var res = this.processResponse(response);
54862                 this.checkFails =0;
54863                 if (!res.success) { // error!
54864                     this.checkFails = 5;
54865                     //console.log('call failure');
54866                     return this.failure(response,opts);
54867                 }
54868                 
54869                 if (!res.data.id) { // id=0 == login failure.
54870                     return this.show();
54871                 }
54872                 
54873                               
54874                         //console.log(success);
54875                 this.fillAuth(res.data);   
54876                 this.checkFails =0;
54877                 Roo.XComponent.build();
54878             },
54879             failure : this.show
54880         });
54881         
54882     }, 
54883     
54884     
54885     check: function(cfg) // called every so often to refresh cookie etc..
54886     {
54887         if (cfg.again) { // could be undefined..
54888             this.checkFails++;
54889         } else {
54890             this.checkFails = 0;
54891         }
54892         var _this = this;
54893         if (this.sending) {
54894             if ( this.checkFails > 4) {
54895                 Roo.MessageBox.alert("Error",  
54896                     "Error getting authentication status. - try reloading, or wait a while", function() {
54897                         _this.sending = false;
54898                     }); 
54899                 return;
54900             }
54901             cfg.again = true;
54902             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
54903             return;
54904         }
54905         this.sending = true;
54906         
54907         Roo.Ajax.request({  
54908             url: this.url,
54909             params: {
54910                 getAuthUser: true
54911             },  
54912             method: this.method,
54913             success:  cfg.success || this.success,
54914             failure : cfg.failure || this.failure,
54915             scope : this,
54916             callCfg : cfg
54917               
54918         });  
54919     }, 
54920     
54921     
54922     logout: function()
54923     {
54924         window.onbeforeunload = function() { }; // false does not work for IE..
54925         this.user = false;
54926         var _this = this;
54927         
54928         Roo.Ajax.request({  
54929             url: this.url,
54930             params: {
54931                 logout: 1
54932             },  
54933             method: 'GET',
54934             failure : function() {
54935                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
54936                     document.location = document.location.toString() + '?ts=' + Math.random();
54937                 });
54938                 
54939             },
54940             success : function() {
54941                 _this.user = false;
54942                 this.checkFails =0;
54943                 // fixme..
54944                 document.location = document.location.toString() + '?ts=' + Math.random();
54945             }
54946               
54947               
54948         }); 
54949     },
54950     
54951     processResponse : function (response)
54952     {
54953         var res = '';
54954         try {
54955             res = Roo.decode(response.responseText);
54956             // oops...
54957             if (typeof(res) != 'object') {
54958                 res = { success : false, errorMsg : res, errors : true };
54959             }
54960             if (typeof(res.success) == 'undefined') {
54961                 res.success = false;
54962             }
54963             
54964         } catch(e) {
54965             res = { success : false,  errorMsg : response.responseText, errors : true };
54966         }
54967         return res;
54968     },
54969     
54970     success : function(response, opts)  // check successfull...
54971     {  
54972         this.sending = false;
54973         var res = this.processResponse(response);
54974         if (!res.success) {
54975             return this.failure(response, opts);
54976         }
54977         if (!res.data || !res.data.id) {
54978             return this.failure(response,opts);
54979         }
54980         //console.log(res);
54981         this.fillAuth(res.data);
54982         
54983         this.checkFails =0;
54984         
54985     },
54986     
54987     
54988     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
54989     {
54990         this.authUser = -1;
54991         this.sending = false;
54992         var res = this.processResponse(response);
54993         //console.log(res);
54994         if ( this.checkFails > 2) {
54995         
54996             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
54997                 "Error getting authentication status. - try reloading"); 
54998             return;
54999         }
55000         opts.callCfg.again = true;
55001         this.check.defer(1000, this, [ opts.callCfg ]);
55002         return;  
55003     },
55004     
55005     
55006     
55007     fillAuth: function(au) {
55008         this.startAuthCheck();
55009         this.authUserId = au.id;
55010         this.authUser = au;
55011         this.lastChecked = new Date();
55012         this.fireEvent('refreshed', au);
55013         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
55014         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
55015         au.lang = au.lang || 'en';
55016         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
55017         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
55018         this.switchLang(au.lang );
55019         
55020      
55021         // open system... - -on setyp..
55022         if (this.authUserId  < 0) {
55023             Roo.MessageBox.alert("Warning", 
55024                 "This is an open system - please set up a admin user with a password.");  
55025         }
55026          
55027         //Pman.onload(); // which should do nothing if it's a re-auth result...
55028         
55029              
55030     },
55031     
55032     startAuthCheck : function() // starter for timeout checking..
55033     {
55034         if (this.intervalID) { // timer already in place...
55035             return false;
55036         }
55037         var _this = this;
55038         this.intervalID =  window.setInterval(function() {
55039               _this.check(false);
55040             }, 120000); // every 120 secs = 2mins..
55041         
55042         
55043     },
55044          
55045     
55046     switchLang : function (lang) 
55047     {
55048         _T = typeof(_T) == 'undefined' ? false : _T;
55049           if (!_T || !lang.length) {
55050             return;
55051         }
55052         
55053         if (!_T && lang != 'en') {
55054             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
55055             return;
55056         }
55057         
55058         if (typeof(_T.en) == 'undefined') {
55059             _T.en = {};
55060             Roo.apply(_T.en, _T);
55061         }
55062         
55063         if (typeof(_T[lang]) == 'undefined') {
55064             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
55065             return;
55066         }
55067         
55068         
55069         Roo.apply(_T, _T[lang]);
55070         // just need to set the text values for everything...
55071         var _this = this;
55072         /* this will not work ...
55073         if (this.form) { 
55074             
55075                
55076             function formLabel(name, val) {
55077                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
55078             }
55079             
55080             formLabel('password', "Password"+':');
55081             formLabel('username', "Email Address"+':');
55082             formLabel('lang', "Language"+':');
55083             this.dialog.setTitle("Login");
55084             this.dialog.buttons[0].setText("Forgot Password");
55085             this.dialog.buttons[1].setText("Login");
55086         }
55087         */
55088         
55089         
55090     },
55091     
55092     
55093     title: "Login",
55094     modal: true,
55095     width:  350,
55096     //height: 230,
55097     height: 180,
55098     shadow: true,
55099     minWidth:200,
55100     minHeight:180,
55101     //proxyDrag: true,
55102     closable: false,
55103     draggable: false,
55104     collapsible: false,
55105     resizable: false,
55106     center: {  // needed??
55107         autoScroll:false,
55108         titlebar: false,
55109        // tabPosition: 'top',
55110         hideTabs: true,
55111         closeOnTab: true,
55112         alwaysShowTabs: false
55113     } ,
55114     listeners : {
55115         
55116         show  : function(dlg)
55117         {
55118             //console.log(this);
55119             this.form = this.layout.getRegion('center').activePanel.form;
55120             this.form.dialog = dlg;
55121             this.buttons[0].form = this.form;
55122             this.buttons[0].dialog = dlg;
55123             this.buttons[1].form = this.form;
55124             this.buttons[1].dialog = dlg;
55125            
55126            //this.resizeToLogo.defer(1000,this);
55127             // this is all related to resizing for logos..
55128             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
55129            //// if (!sz) {
55130              //   this.resizeToLogo.defer(1000,this);
55131              //   return;
55132            // }
55133             //var w = Ext.lib.Dom.getViewWidth() - 100;
55134             //var h = Ext.lib.Dom.getViewHeight() - 100;
55135             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
55136             //this.center();
55137             if (this.disabled) {
55138                 this.hide();
55139                 return;
55140             }
55141             
55142             if (this.user.id < 0) { // used for inital setup situations.
55143                 return;
55144             }
55145             
55146             if (this.intervalID) {
55147                 // remove the timer
55148                 window.clearInterval(this.intervalID);
55149                 this.intervalID = false;
55150             }
55151             
55152             
55153             if (Roo.get('loading')) {
55154                 Roo.get('loading').remove();
55155             }
55156             if (Roo.get('loading-mask')) {
55157                 Roo.get('loading-mask').hide();
55158             }
55159             
55160             //incomming._node = tnode;
55161             this.form.reset();
55162             //this.dialog.modal = !modal;
55163             //this.dialog.show();
55164             this.el.unmask(); 
55165             
55166             
55167             this.form.setValues({
55168                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
55169                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
55170             });
55171             
55172             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
55173             if (this.form.findField('username').getValue().length > 0 ){
55174                 this.form.findField('password').focus();
55175             } else {
55176                this.form.findField('username').focus();
55177             }
55178     
55179         }
55180     },
55181     items : [
55182          {
55183        
55184             xtype : 'ContentPanel',
55185             xns : Roo,
55186             region: 'center',
55187             fitToFrame : true,
55188             
55189             items : [
55190     
55191                 {
55192                
55193                     xtype : 'Form',
55194                     xns : Roo.form,
55195                     labelWidth: 100,
55196                     style : 'margin: 10px;',
55197                     
55198                     listeners : {
55199                         actionfailed : function(f, act) {
55200                             // form can return { errors: .... }
55201                                 
55202                             //act.result.errors // invalid form element list...
55203                             //act.result.errorMsg// invalid form element list...
55204                             
55205                             this.dialog.el.unmask();
55206                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
55207                                         "Login failed - communication error - try again.");
55208                                       
55209                         },
55210                         actioncomplete: function(re, act) {
55211                              
55212                             Roo.state.Manager.set(
55213                                 this.dialog.realm + '.username',  
55214                                     this.findField('username').getValue()
55215                             );
55216                             Roo.state.Manager.set(
55217                                 this.dialog.realm + '.lang',  
55218                                 this.findField('lang').getValue() 
55219                             );
55220                             
55221                             this.dialog.fillAuth(act.result.data);
55222                               
55223                             this.dialog.hide();
55224                             
55225                             if (Roo.get('loading-mask')) {
55226                                 Roo.get('loading-mask').show();
55227                             }
55228                             Roo.XComponent.build();
55229                             
55230                              
55231                             
55232                         }
55233                     },
55234                     items : [
55235                         {
55236                             xtype : 'TextField',
55237                             xns : Roo.form,
55238                             fieldLabel: "Email Address",
55239                             name: 'username',
55240                             width:200,
55241                             autoCreate : {tag: "input", type: "text", size: "20"}
55242                         },
55243                         {
55244                             xtype : 'TextField',
55245                             xns : Roo.form,
55246                             fieldLabel: "Password",
55247                             inputType: 'password',
55248                             name: 'password',
55249                             width:200,
55250                             autoCreate : {tag: "input", type: "text", size: "20"},
55251                             listeners : {
55252                                 specialkey : function(e,ev) {
55253                                     if (ev.keyCode == 13) {
55254                                         this.form.dialog.el.mask("Logging in");
55255                                         this.form.doAction('submit', {
55256                                             url: this.form.dialog.url,
55257                                             method: this.form.dialog.method
55258                                         });
55259                                     }
55260                                 }
55261                             }  
55262                         },
55263                         {
55264                             xtype : 'ComboBox',
55265                             xns : Roo.form,
55266                             fieldLabel: "Language",
55267                             name : 'langdisp',
55268                             store: {
55269                                 xtype : 'SimpleStore',
55270                                 fields: ['lang', 'ldisp'],
55271                                 data : [
55272                                     [ 'en', 'English' ],
55273                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
55274                                     [ 'zh_CN', '\u7C21\u4E2D' ]
55275                                 ]
55276                             },
55277                             
55278                             valueField : 'lang',
55279                             hiddenName:  'lang',
55280                             width: 200,
55281                             displayField:'ldisp',
55282                             typeAhead: false,
55283                             editable: false,
55284                             mode: 'local',
55285                             triggerAction: 'all',
55286                             emptyText:'Select a Language...',
55287                             selectOnFocus:true,
55288                             listeners : {
55289                                 select :  function(cb, rec, ix) {
55290                                     this.form.switchLang(rec.data.lang);
55291                                 }
55292                             }
55293                         
55294                         }
55295                     ]
55296                 }
55297                   
55298                 
55299             ]
55300         }
55301     ],
55302     buttons : [
55303         {
55304             xtype : 'Button',
55305             xns : 'Roo',
55306             text : "Forgot Password",
55307             listeners : {
55308                 click : function() {
55309                     //console.log(this);
55310                     var n = this.form.findField('username').getValue();
55311                     if (!n.length) {
55312                         Roo.MessageBox.alert("Error", "Fill in your email address");
55313                         return;
55314                     }
55315                     Roo.Ajax.request({
55316                         url: this.dialog.url,
55317                         params: {
55318                             passwordRequest: n
55319                         },
55320                         method: this.dialog.method,
55321                         success:  function(response, opts)  {  // check successfull...
55322                         
55323                             var res = this.dialog.processResponse(response);
55324                             if (!res.success) { // error!
55325                                Roo.MessageBox.alert("Error" ,
55326                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
55327                                return;
55328                             }
55329                             Roo.MessageBox.alert("Notice" ,
55330                                 "Please check you email for the Password Reset message");
55331                         },
55332                         failure : function() {
55333                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
55334                         }
55335                         
55336                     });
55337                 }
55338             }
55339         },
55340         {
55341             xtype : 'Button',
55342             xns : 'Roo',
55343             text : "Login",
55344             listeners : {
55345                 
55346                 click : function () {
55347                         
55348                     this.dialog.el.mask("Logging in");
55349                     this.form.doAction('submit', {
55350                             url: this.dialog.url,
55351                             method: this.dialog.method
55352                     });
55353                 }
55354             }
55355         }
55356     ]
55357   
55358   
55359 })
55360  
55361
55362
55363